第三章:Android之NDK的使用

前面两章介绍了android基础环境的搭建,下面以实现一个计算器的例子作为实战的过程

笔者不会介绍基础的Android语法和基本配置问题,这些基础可以查阅的资料很多,就不再献丑了。

第三章 Android之NDK的使用

宏观展示效果图


一、Android 的计算器页面实现

1、页面元素配置文件,activity_main.xml 内容如下:

<?xml version="1.0" encoding="utf-8"?>

 <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:orientation="horizontal"
        android:rowCount="5"
        android:columnCount="4"
        android:id="@+id/grid" >

    <EditText
        android:id="@+id/result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_columnSpan="4"
        android:layout_gravity="fill"
        android:enabled="false"
        android:inputType="text|textMultiLine"
        android:text="@string/init_default" >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/btn_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/one" />
    <Button
        android:id="@+id/btn_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/two" />
    <Button
        android:id="@+id/btn_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/three" />
    <Button
        android:id="@+id/btn_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/add" />
    
    <Button
        android:id="@+id/btn_four"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/four" />
    <Button
        android:id="@+id/btn_five"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/five" />
    <Button
        android:id="@+id/btn_six"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/six" />
    <Button
        android:id="@+id/btn_sub"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/sub" />
    
    <Button
        android:id="@+id/btn_seven"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/seven" />
    <Button
        android:id="@+id/btn_eight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/eight" />
    <Button
        android:id="@+id/btn_nine"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/nine" />
    <Button
        android:id="@+id/btn_mul"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/mul" />
    
    <Button
        android:id="@+id/btn_zero"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/zero" />
    <Button
        android:id="@+id/btn_equal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_columnSpan="2"
        android:layout_gravity="fill"
        android:text="@string/equal" />
    <Button
        android:id="@+id/btn_div"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_rowSpan="2"
        android:layout_gravity="fill"
        android:text="@string/div" />
    <Button
        android:id="@+id/btn_clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_columnSpan="2"
        android:layout_gravity="fill"
        android:text="@string/clear" />
    <Button
        android:id="@+id/btn_mod"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/mod" />
 </GridLayout>

哪一种Layout不重要,重要的是如何在页面中构造整齐的按钮排放,为了简单,笔者采用GridLayout布局,因为它可以自带表格对齐,

相对其它布局,在这方面的实现上要简单许多。


2、事件响应逻辑 MainActivity.java 的实现内容

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

	static {
		System.loadLibrary("HBIntegerVM");
	}

	private EditText result = null;
	private Button btn_one = null;
	private Button btn_two = null;
	private Button btn_three = null;
	private Button btn_four = null;
	private Button btn_five = null;
	private Button btn_six = null;
	private Button btn_seven = null;
	private Button btn_eight = null;
	private Button btn_nine = null;
	private Button btn_zero = null;
	private Button btn_add = null;
	private Button btn_sub = null;
	private Button btn_mul = null;
	private Button btn_div = null;
	private Button btn_mod = null;
	private Button btn_equal = null;
	private Button btn_clear = null;
	private String opt_num1 = "", opt_num2 = "", opt_result="";
	private boolean second_entry = false;

	private enum CALC_OPT {
		ADD, SUB, MUL, DIV, MOD
	}

	private CALC_OPT opt;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		result = (EditText) super.findViewById(R.id.result);
		opt_num1 = result.getText().toString();

		btn_one = (Button) super.findViewById(R.id.btn_one);
		btn_one.setOnClickListener(new showOnClickListener());
		btn_two = (Button) super.findViewById(R.id.btn_two);
		btn_two.setOnClickListener(new showOnClickListener());
		btn_three = (Button) super.findViewById(R.id.btn_three);
		btn_three.setOnClickListener(new showOnClickListener());
		btn_four = (Button) super.findViewById(R.id.btn_four);
		btn_four.setOnClickListener(new showOnClickListener());
		btn_five = (Button) super.findViewById(R.id.btn_five);
		btn_five.setOnClickListener(new showOnClickListener());
		btn_six = (Button) super.findViewById(R.id.btn_six);
		btn_six.setOnClickListener(new showOnClickListener());
		btn_seven = (Button) super.findViewById(R.id.btn_seven);
		btn_seven.setOnClickListener(new showOnClickListener());
		btn_eight = (Button) super.findViewById(R.id.btn_eight);
		btn_eight.setOnClickListener(new showOnClickListener());
		btn_nine = (Button) super.findViewById(R.id.btn_nine);
		btn_nine.setOnClickListener(new showOnClickListener());
		btn_zero = (Button) super.findViewById(R.id.btn_zero);
		btn_zero.setOnClickListener(new showOnClickListener());

		btn_add = (Button) super.findViewById(R.id.btn_add);
		btn_add.setOnClickListener(new optOnClickListener());
		btn_sub = (Button) super.findViewById(R.id.btn_sub);
		btn_sub.setOnClickListener(new optOnClickListener());
		btn_mul = (Button) super.findViewById(R.id.btn_mul);
		btn_mul.setOnClickListener(new optOnClickListener());
		btn_div = (Button) super.findViewById(R.id.btn_div);
		btn_div.setOnClickListener(new optOnClickListener());
		btn_mod = (Button) super.findViewById(R.id.btn_mod);
		btn_mod.setOnClickListener(new optOnClickListener());
		btn_equal = (Button) super.findViewById(R.id.btn_equal);
		btn_equal.setOnClickListener(new equalOnClickListener());
		btn_clear = (Button) super.findViewById(R.id.btn_clear);
		btn_clear.setOnClickListener(new OnClickListener(){
			public void onClick(View v) {
				opt_num1 = "0";
				opt_num2 = "";
				opt_result = "";
				result.setText(opt_num1);
				second_entry = false;
			}
		});

	}

	private class MyHBInteger extends ICalcHBInteger {
	}

	private class showOnClickListener implements OnClickListener {

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			if(second_entry) {
				if(opt_num2.equals("0")){
					opt_num2 = ((Button)v).getText().toString();
				} else {
					opt_num2 += ((Button)v).getText().toString();
				}
				result.setText(opt_num2);
			} else {
				if(opt_result.length() != 0) {
					if(opt_num2.equals("0")){
						opt_num2 = ((Button)v).getText().toString();
					} else {
						opt_num2 += ((Button)v).getText().toString();
					}
					result.setText(opt_num2);
				} else {
					if(opt_num1.equals("0")){
						opt_num1 = ((Button)v).getText().toString();
					} else {
						opt_num1 += ((Button)v).getText().toString();
					}
					result.setText(opt_num1);
				}
			}			
		}
	}
	
	
	private class optOnClickListener implements OnClickListener {
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			switch(v.getId()){
			case R.id.btn_add:
				opt = CALC_OPT.ADD;
				break;
			case R.id.btn_sub:
				opt = CALC_OPT.SUB;
				break;
			case R.id.btn_mul:
				opt = CALC_OPT.MUL;
				break;
			case R.id.btn_div:
				opt = CALC_OPT.DIV;
				break;
			case R.id.btn_mod:
				opt = CALC_OPT.MOD;
				break;
			default: break;
			}
			
			if(second_entry){
				MyHBInteger myhb = new MyHBInteger();
				int resultLength = 0;
				if(CALC_OPT.MUL != opt){
					resultLength = opt_num1.length() > opt_num2.length() ? opt_num1.length() + 1 : opt_num2.length() + 1;
				} else {
					resultLength = opt_num1.length() * opt_num2.length();
				}
				myhb.init_HBInt(resultLength);
				switch (opt) {
				case ADD:
					myhb.add_HBInt(opt_num1, opt_num2);
					break;
				case SUB:
					myhb.sub_HBInt(opt_num1, opt_num2);
					break;
				case MUL:
					myhb.mul_HBInt(opt_num1, opt_num2);
					break;
				case DIV:
					myhb.div_HBInt(opt_num1, opt_num2);
					break;
				case MOD:
					myhb.mod_HBInt(opt_num1, opt_num2);
					break;
				default:
					break;
				}
				opt_result = myhb.output_HBInt();
				result.setText(opt_result);
				myhb.release_HBInt();
				opt_num1 = opt_result;
				opt_num2 = "";
			} else second_entry = true;
		}
	}
	
	private class equalOnClickListener implements OnClickListener {
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			
			if (opt_num1.length() == 0 || opt_num2.length() == 0) {
				Toast.makeText(getApplicationContext(), "No second number!",
						Toast.LENGTH_SHORT).show();
				return;
			} else {
				MyHBInteger myhb = new MyHBInteger();
				int resultLength = 0;
				if (opt == CALC_OPT.MUL)
					resultLength = opt_num1.length() * opt_num2.length();
				else
					resultLength = opt_num1.length() > opt_num2.length() ? opt_num1
							.length() + 1 : opt_num2.length() + 1;
				myhb.init_HBInt(resultLength);
				switch (opt) {
				case ADD:
					myhb.add_HBInt(opt_num1, opt_num2);
					break;
				case SUB:
					myhb.sub_HBInt(opt_num1, opt_num2);
					break;
				case MUL:
					myhb.mul_HBInt(opt_num1, opt_num2);
					break;
				case DIV:
					myhb.div_HBInt(opt_num1, opt_num2);
					break;
				case MOD:
					myhb.mod_HBInt(opt_num1, opt_num2);
					break;
				default:
					break;
				}
				opt_result = myhb.output_HBInt();
				result.setText(opt_result);
				myhb.release_HBInt();
				opt_num1 = opt_result;
				opt_num2 = "";
				second_entry = false;
			}			
		}

	}
}

二、NDK的使用

针对以上代码中实现的部分,逻辑上已经完成,但是核心部分的运算却没有出现,那是因为真正的计算部分是采用C去实现,然后由Java侧调用,即:

static {
	System.loadLibrary("HBIntegerVM");
	}
将C实现的计算逻辑采用动态库的方式被Java侧调用,这就是JNI的基本原理。

下面就如何实现调用与被调用的过程做一个实战介绍

首先得有已经实现运算逻辑的C侧实现,比如笔者已经实现了+,-,*,/,%等基本方法,在Java侧这里定义的名称为:

public abstract class ICalcHBInteger {
    public native int init_HBInt(int size);
    public native int release_HBInt();
    public native void input_HBInt(String in);
    public native String output_HBInt();
    public native String add_HBInt(String a1,String a2);
    public native String sub_HBInt(String s1,String s2);
    public native String mul_HBInt(String m1,String m2);
    public native String div_HBInt(String d1,String d2);
    public native String mod_HBInt(String mod1,String mod2);
    public native String powerMod_HBInt(String p1,String p2, String p3);
}
利用JNI解析命令: javah -classpath bin/classes -d jni net.yjbqzsf.android.ICalcHBInteger

-classpath:表示类的路劲

-d : 表示生成的头文件存放的目录

net.yjbqzsf.android.ICalcHBInteger 则是完整类名,读者根据自己的实际情况配置

注意:执行javah命令是在该工程的根目录下,如果没有jni目录,则需要手动创建

执行成功后,会在jni目录下生成 net_yjbqzsf_android_ICalcHBInteger.h

这个头文件就是与Java侧交互的接口,C侧要被调用的部分,就要包含该头文件去实现(不明白原理的可以搜索JNI技术)

然后在自定义的C侧源码文件需要实现这个头文件中申明的方法。

利用NDK进行编译

所有的C侧源文件为了方便都需要在jni中存放,至于目录结构如何管理,读者根据自己的习惯安排。

笔者的jni路径下文件列表为:

Android.mk         HBInteger.c    net_yjbqzsf_android_ICalcHBInteger.h
Application.mk   HBInteger.h
common.h           HBIntegerVM.c

其中需要修改的是 Android.mk 与 Application.mk (不存在的可以从NDK安装路径的 $NDK/samples/hello-jni/jni 目录下拷贝)

其中Android.mk 修改配置如下:
LOCAL_MODULE    := HBIntegerVM
LOCAL_SRC_FILES := HBIntegerVM.c HBInteger.c

Application.mk修改配置如下:

APP_ABI := x86

(因为笔者的机器是x86架构,且采用虚拟设备,读者如果有真机,根据真机的架构配置,无外乎arm与mips)

在工程的根目录下执行 ndk-build 命令,前提是第二章中介绍的配置是正确的。

如果一切正常,则在工程根目录的libs目录下生成 x86 / libHBIntegerVM.so 这样的目录与动态库。

好了,到此你的配置任务初步完成,直接运行工程Run as -> Android Application 吧,见证你的NDK项目运行情况。


小结

NDK可以自动在eclipse中运行,但是笔者没有这样做,因为在工作量且不复杂的情况下还是手动操作能感受的更多。

之前笔者使用JNI也是手动使用gcc进行编译后拷贝动态库至目标工程的适当位置(需要设置Native Location,或者直接libs目录),

在手动编译的过程中,任何一点疏忽都会导致不成功,只有在不断失败的基础之上,我们才能懂的更多,走的更远。


未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值