使用命令行工具构建 Android APK

新建工程

  • 查看构建目标

输入下面的命令查看所以可构建目标:

$android list targets

可能的输出:

Available Android targets:
----------
id: 1 or "android-3"
     Name: Android 1.5
     Type: Platform
     API level: 3
     Revision: 4
     Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
     ABIs : armeabi
----------
id: 2 or "Google Inc.:Google APIs:3"
     Name: Google APIs
     Type: Add-On
     Vendor: Google Inc.
     Revision: 3
     Description: Android + Google APIs
     Based on Android 1.5 (API level 3)
     Libraries:
      * com.google.android.maps (maps.jar)
          API for Google Maps
     Skins: QVGA-P, HVGA-L, HVGA (default), QVGA-L, HVGA-P
     ABIs : armeabi
----------
id: 3 or "android-11"
     Name: Android 3.0
     Type: Platform
     API level: 11
     Revision: 2
     Skins: WXGA (default)
     ABIs : armeabi
(等等,不一一列出)
  • 新建工程

在上一步的结果中选择一个构建目标,纪录下id后的数字,作为下面命令的target参数

$ android create yourProjectName \
> --target 1 \
> --path yourProjectFolder  \
> --activity yourActivity \
> --package com.yourdomain.project \

如果一切配置都正确,一个android工程就创建完成了。

更新工程

如果需要把工程由id:1更新到id:3,可以使用下列命令:

$ android update yourProjectName \
> --target 2 \
> --path  yourProjectFolder

构建工程

$ ant或者
$ ant debug

Android程序调用JNI

OS: MAC OS 10.7.5 Android: 4.3 NDK: android-ndk-r7c java: 1.6.0_51

使用JNI

在Eclipse中新建一个Android工程,假设项目目录为project,修改activity_main.xml 如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView android:id="@+id/showText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

MainActivity.java:

package net.lnmcc.usejni;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {
        // 加载JNI库
	static {
		System.loadLibrary("useJNI");
	}
	// 定义JNI方法 
	public native String getStringFromNative();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 调用JNI方法 
		String strResult = getStringFromNative();
		TextView tv = (TextView)findViewById(R.id.showText);
		tv.setText(strResult);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

创建Application.mk

在项目根目录中创建Application.mk:

APP_PROJECT_PATH:=$(call my-dir)  #项目所在目录
APP_MODULES:=useJNI #应用程序的名字

编译上面的代码,在 bin/classes/net/lnmcc/usejni下生成了相应的class文件。

实现JNI

JNI头文件的生成

在项目根目录下新建jni文件夹,用来放置jni相关的文件。 JNI头文件可以使用javah来自动生成,这里的关键是路径一定要正确,:

$ cd project
$ javah -classpath bin/classes/  \
> -d jni  \
>  com.lnmcc.usejni.MainActivity #前缀一定是你的包名,否则找不到class文件

如果没有错误,javah就会自动生成net_lnmcc_usejni_MainActivity.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class net_lnmcc_usejni_MainActivity */

#ifndef _Included_net_lnmcc_usejni_MainActivity
#define _Included_net_lnmcc_usejni_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     net_lnmcc_usejni_MainActivity
 * Method:    getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_net_lnmcc_usejni_MainActivity_getStringFromNative
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

实现JNI函数

在project/jni文件夹中新建C源代码文件,JNI函数的命名规则为:

JAVA_包名_类名_函数名

useJNI.c:

#include "net_lnmcc_usejni_MainActivity.h"

JNIEXPORT jstring JNICALL Java_net_lnmcc_usejni_MainActivity_getStringFromNative
  (JNIEnv *env, jobject obj) {

	return (*env)->NewStringUTF(env, (char*)"Hello, World");
}

创建Android.mk

在project/jni/文件夹中创建Android.mk文件:

LOCAL_PATH:=$(call my-dir) #项目根目录

include $(CLEAR_VARS) #除了LOCAL_PATH,清除其它LOCAL_变量的值

LOCAL_MODULE:=useJNI
LOCAL_SRC_FILES:=useJNI.c

include $(BUILD_SHARED_LIBRARY) # 编译成动态库

编译JNI库

$ cd project/jni
$ ndk-build #这是NDK-r7c提供的工具,确保在环境变量中

如果没有错误,输出:

Compile thumb  : useJNI <= useJNI.c
SharedLibrary  : libuseJNI.so
Install        : libuseJNI.so => libs/armeabi/libuseJNI.so

测试程序

在Eclipse中刷新项目,使在project/libs/armeabi/下出现libuseJNI.so,然后Run程序 。

WordPress twentytwelve主题修改页面宽度

Wordpress twentytwelve theme以简洁取胜,但是这个主题是固定页面宽度的(默认值为960),如果网站上放置的东西多一点就会显得拥挤不堪,破坏了其整体整洁性,下面介绍的方法可以很方便的更改这个主题的宽度:

  • 打开主题的style.css文件(可以使用WP后台编辑也可以直接进主机目录修改 一个可能的路径 /var/www/wordpress/wp-content/themes/twentytwelve

  • 查找下面的内容(在我的主机上是在655行)

/* Footer */
footer[role="contentinfo"] {
        border-top: 1px solid #ededed;                        
        clear: both;
        font-size: 12px;
        font-size: 0.857142857rem;                            
        line-height: 2;
        max-width: 960px;
        max-width: 68.571428571rem;                           
        margin-top: 24px;
        margin-top: 1.714285714rem;                           
        margin-left: auto;
        margin-right: auto;                                   
        padding: 24px 0;
        padding: 1.714285714rem 0;                            
}

把其中的max-width修改成符合你要求的数值,px和rem之间的换算关系可以简单的用px / 14来计算,比如1000px / 14 = 71.4285714rem 。

  • 查找下面这一段(在我的主机上是在1420行)
/* Minimum width of 600 pixels. */
@media screen and (min-width: 600px) {
        .author-avatar {
                float: left;
                margin-top: 8px;
                margin-top: 0.571428571rem;
        }
        .author-description {
                float: right;
                width: 80%;
        }
        .site {
                margin: 0 auto;
                max-width: 960px;
                max-width: 68.571428571rem;
                overflow: hidden;
        }

同上一步修改max-width 。

Done!

基于QWebView的浏览器Widget

OS : ubuntu 12.04 QT : 4.8.4 //测试时发现QT5的QWebView在加载Flash时会出现黑块,速度也比4.8慢很多。

如果有不能打开网页图片的情况,可能是因为你的QT找不到图片插件,解决方法如下:

  • 导入QT图片插件路径:
export QT_PLUGIN_PATH=/usr/lib/qt4/plugins
  • 拷贝图片插件到工程目录:
cp -av /usr/lib/qt4/plugins/imageformats/   ~/Projects/browser   <----你自己的工程目录

BrowserWidget.pro

#只是一个Demo用以测试QWebView
QT       += core gui webkit webkitwidgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = BrowserWidget
TEMPLATE = app

SOURCES += main.cpp \
    MyWebView.cpp \
    MyBrowser.cpp

HEADERS += \
    MyWebView.h \
    MyBrowser.h

MyBrowser.h

#ifndef MYBROWSER_H
#define MYBROWSER_H

#include "MyWebView.h"
#include <QWidget>
#include <QPushButton>

class MyBrowser : public QWidget {
    Q_OBJECT

public:
    explicit MyBrowser(QUrl url, QWidget *parent = 0);
    virtual ~MyBrowser();

private:
    void addButtons();
    void loadWebView(QUrl url, QWidget *parent = 0);

private slots:
    void closeSlot();

private:
    QPushButton *m_btnBack;
    QPushButton *m_btnForward;
    QPushButton *m_btnClose;

    MyWebView *m_webView;

    QUrl m_url;
};

#endif // MYBROWSER_H

MyBrowser.cpp

#include "MyBrowser.h"
#include <QDebug>

MyBrowser::MyBrowser(QUrl url, QWidget *parent) : QWidget(parent) {

    m_url = url;

    loadWebView(m_url, this);
    addButtons();
}

MyBrowser::~MyBrowser() {
    delete m_btnBack;
    delete m_btnClose;
    delete m_btnForward;
    delete m_webView;
}

void MyBrowser::addButtons() {

    m_btnBack = new QPushButton("back", this);
    connect(m_btnBack, SIGNAL(released()), m_webView, SLOT(back()));
    m_btnBack->setGeometry(QRect(700, 0, 100, 30));

    m_btnForward = new QPushButton("Forward", this);
    connect(m_btnForward, SIGNAL(released()), m_webView, SLOT(forward()));
    m_btnForward->setGeometry(QRect(810, 0, 100, 30));

    m_btnClose = new QPushButton("Close", this);
    /* 如果开启下面这条,那么在点击close的时候只是关闭了QWebView, 不会关闭QWidget */
    /* 当把这个Browser嵌入到其他Widget的时候, 这个Browser Wigget还是会显示在界面上 */
    //connect(m_btnClose, SIGNAL(released()), m_webView, SLOT(close()));
    
    /* 调用我们自己的SLOT, 以关闭整个Browser Widget */
    connect(m_btnClose, SIGNAL(released()), this, SLOT(close()));
    m_btnClose->setGeometry(QRect(920, 0, 100, 30));
}

void MyBrowser::loadWebView(QUrl url, QWidget *parent) {
    m_webView = new MyWebView(parent);
    m_webView->load(url);
    m_webView->setGeometry(QRect(0, 0, 1920, 1080));
}

void MyBrowser::closeSlot() {
    close();
}

MyWebView.h

#ifndef MYWEBVIEW_H
#define MYWEBVIEW_H

#include <QtWebKit/QWebView>

class MyWebView : public QWebView {
   Q_OBJECT

public:
    explicit MyWebView(QWidget *parent = 0);
    virtual ~MyWebView();

    QWebView *createWindow(QWebPage::WebWindowType type);

private slots:
    void linkClickedSlot(QUrl url);
};

#endif // MYWEBVIEW_H

MyWebView.cpp

#include "MyWebView.h"
#include <QtNetwork/QtNetwork>
#include <QtNetwork/QHttpRequestHeader>
#include <QDesktopServices>
#include <QtWebKit/QWebPage>

MyWebView::MyWebView(QWidget *parent)
    : QWebView(parent) {
/* 网页缓存机制, 这里没有使用 */
#if 0
    QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    manager = page()->networkAccessManager();
    QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
    diskCache->setCacheDirectory(location);
    manager->setCache(diskCache);
    qDebug << diskCache->cacheDirectory() << diskCache->cacheSize();
    page()->setNetworkAccessManager(manager);
    page()->settings()->setMaximumPagesInCache(10);
    page()->settings()->setAttribute(QWebSettings::AutoLoadImages, true);
    page()->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
#endif
    /* 如果需要访问flash网页,就必须开启下面这条,当然前提是你的机器上安装了flashplayer */
    settings()->setAttribute(QWebSettings::PluginsEnabled, true);
    settings()->setAttribute(QWebSettings::JavaEnabled, true);
    settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
    settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
    settings()->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
    settings()->setFontFamily(QWebSettings::SansSerifFont, "SansSerifFont");
    /* 当页面上的链接被点击的时候,可能会打开一个新窗口也可能是在原窗口中打开   */
    /* 这里我们需要一直在原窗口中打开链接,所以需要我们自己处理点击链接时的动作 */
    /* 设置DelegateAllLinks告知QWebView我们自己处理所有页面点击动作,并且   */
    /* 设置了对应的槽                                                   */
    page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    connect(this, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClickedSlot(QUrl)));
}

MyWebView::~MyWebView() {

}
/* 当QWebView需要创建新窗口的时候会调用这个函数 */
QWebView *MyWebView::createWindow(QWebPage::WebWindowType type) {
    /* 如果开启下面3条,当页面链接被点击,创建一个新窗口, 原窗口不会消失 */
    //MyWebView *p = new MyWebView();
    //p->setGeometry(this->geometry());
    //p->show();
    /* 这里返回this,即复用当前窗口,不开启新窗口 */
    return this;
}

void MyWebView::linkClickedSlot(QUrl url) {
    load(url);
}

main.cpp

#include "MyBrowser.h"
#include <QApplication>
#include <QTextCodec>
#include <QtWebKit/QWebView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyBrowser *browser = new MyBrowser(QUrl("http://www.vs.com.cn/"));
    browser->showFullScreen();

    return a.exec();
}

Android平台YUV420SP到RGB565的转换

	/**
	 * @param yuvDataArray
	 * @param width 
	 * @param height
	 * @return int[] : ARGB array
	 * @throws NullPointerException
	 * @throws IllegalArgumentException
	 */

	private static int[] decodeYUV(byte[] yuvDataArray, int width, int height) 
			throws NullPointerException, IllegalArgumentException {
		
		int size = width * height;
		
		if(yuvDataArray == null)
			throw new NullPointerException("buffer yuvDataArray is null");
		if(yuvDataArray.length < size)
			throw new IllegalArgumentException("buffer yuvDataArray size"
					+ yuvDataArray.length
					+ "< minium " + size * 3 / 2);
		int[] out = new int[size];
		int i, j;
		int Y, Cr = 0, Cb = 0;
		for(j = 0; j < height; j++) {
			int pixelIdx = j * width;
			int jDiv2 = j >> 1;
		
			for(i = 0; i < width; i++) {
				Y = yuvDataArray[pixelIdx];
				if(Y < 0) 
					Y += 255;
				if((i & 0x1) != 1) {
					int cOff = size + jDiv2 * width + (i >> 1) *2;
					Cb = yuvDataArray[cOff];
					if(Cb < 0) {
						Cb += 127;
					} else {
						Cb -= 128;
					}
					Cr = yuvDataArray[cOff + 1];
					if(Cr < 0) {
						Cr += 127;
					} else {
						Cr -= 128;
					}
				}
				
				int R = Y + Cr + (Cr >> 2) + (Cr >> 3) + (Cr >> 5);
				if(R < 0) {
					R = 0;
				} else if(R > 255) {
					R = 255;
				}
				
				int G = Y - (Cb >> 2) + (Cb >> 4) + (Cb >> 5) - (Cr >> 1) + (Cr >> 3) + (Cr >> 4) + (Cr >> 5);
				if(G < 0) {
					G = 0;
				} else if(G > 255) {
					G = 255;
				}
				
				int B = Y + Cb + (Cb >> 1) + (Cb >> 2) + (Cb >> 6);
				if(B < 0) {
					B = 0;
				} else if(B > 255) {
					B = 255;
				}
				
				out[pixelIdx++] = 0xff000000 + (B << 16) + (G << 8) + R;
			}
		}
		return out;
	}