[置顶] jni学习之三--数据传递

现在很多公司做产品,一款产品往往存在很多平台,比如有安卓、苹果、黑莓、塞班、wp等。这些平台都要实现的话,往往需要很多人力和财力,而且质量可能也不高,于是常见的方式就是做中间件来适配这些平台。我们只要一个中间件平台,将这些平台共同需要的功能抽到中间件去实现。上面这些平台开发的语言不尽相同,综合效率和通用性我们一般都是选择C/c++来实现这个中间件,所需要注意的就是适配的问题。对苹果、黑莓、塞班等还是比较容易适配,因为他们主要还是基于C相关的,但是对于安卓和wp,适配就相对麻烦多了,wp咋们暂且不管,我们重点来看下安卓,安卓上层开发是用java开发的,使java和c/c++联系起来这里就需要用到NDK开发了。

        对于ndk开发,主要就是关注两件事情,如何实现java层传数据给C/c++以及如何实现C/C++传数据给java,比如:登陆注册的时候,你在上层java实现的一个activity界面输入用户名和密码(这里就是数据),这个时候自然就是你传数据给c/c++,然后通过C/C++(这里你可以看成是中间件了)向服务端发数据,服务端根据收到的数据与之前注册的数据进行匹配,匹配成功就返回数据给中间件,中间件再返回给上层。

    接下来我们根据具体事例来给大家介绍一下这个过程。

  我们以备份数据到云端和还原通讯录到本地来说明

1、上层的activity界面代码实现

package com.afmobi.palmchat.ui.activity.setting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.afmobi.palmchat.BaseActivity;
import com.afmobi.palmchat.PalmchatApp;
import com.afmobi.palmchat.R;
import com.afmobi.palmchat.constant.JsonConstant;
import com.afmobi.palmchat.constant.RequestConstant.RequestType;
import com.afmobi.palmchat.listener.ReqCodeListener;
import com.afmobi.palmchat.network.Response;


import com.afmobi.palmchat.ui.activity.setting.Query.OnQueryComplete;
import com.afmobi.palmchat.util.DialogUtils;
import com.afmobi.palmchat.util.ToastManager;
import com.core.AfNearByGpsInfo;
import com.core.AfPalmchat;
import com.core.AfPbInfo;
import com.core.Consts;
import com.core.cache.CacheManager;
import com.core.listener.AfHttpResultListener;

public class BackupActivity extends BaseActivity implements OnClickListener,
		AfHttpResultListener, ReqCodeListener, OnQueryComplete {

	private View viewBackup;
	private View viewRecovery;
	public final static int startCode = 1;
	ProgressDialog progress;
	private Button mBtnBack;
	private AfPalmchat mAfCorePalmchat;
	private byte action;
	private Query queryHandler;
	private List<Contacts> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
	}

	private void setParams() {
		findViewById(R.id.back_layout).setOnClickListener(this);
		viewBackup = findViewById(R.id.backup);
		viewRecovery = findViewById(R.id.recovery);
		viewBackup.setOnClickListener(this);
		viewRecovery.setOnClickListener(this);
	}

	@Override
	public void findViews() {
		getContacts();
		// TODO Auto-generated method stub
		mAfCorePalmchat = ((PalmchatApp) getApplication()).mAfCorePalmchat;
		setContentView(R.layout.phone_asisstant);
		((TextView) findViewById(R.id.title_text))
				.setText(R.string.phonebook_backup);
		setParams();

		mBtnBack = (Button) this.findViewById(R.id.back_button);
		mBtnBack.setOnClickListener(this);
		progress = new ProgressDialog(this);
		progress.setMax(100);
		progress.setMessage(getString(R.string.restore_address_book));
		progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
	}

	@Override
	public void init() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		if (v == viewBackup) {//备份到云端
			DialogUtils.confirmDialog(this,
					getString(R.string.back_up_address_book),
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
				
							List<Contacts> locallist = CacheManager
									.getInstance().getContacts();
							if(locallist==null){
								return;
							}
							if (locallist.size() == 0) {
								locallist = BackupActivity.this.list;
								CacheManager.getInstance().setContacts(
										locallist);
							}
							if (locallist.size() == 0) {
								ToastManager.getInstance().show(
										BackupActivity.this,
										getString(R.string.address_book_empty));
							} else {
								Contacts contacts = null;
								String pb_name[] = new String[locallist.size()];
								String pb_phone[] = new String[locallist.size()];

								for (int i = 0; i < locallist.size(); i++) {
									contacts = locallist.get(i);
									pb_name[i] = contacts.name;
									pb_phone[i] = contacts.number;

								}
								showProgressDialog(getString(R.string.loading));
								mAfCorePalmchat.AfHttpPhonebookBackup(pb_name,
										pb_phone, Consts.HTTP_ACTION_A, 0, 0, 0,
										BackupActivity.this);

							}

						}

					});
		} else if (v == viewRecovery) {//恢复到本地
			// 恢复 Are you sure to restore phonebook?
			DialogUtils.confirmDialog(this,
					getString(R.string.sure_to_restore),
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							showProgressDialog(getString(R.string.loading));
								mAfCorePalmchat.AfHttpPhonebookBackup(null,
										null, Consts.HTTP_ACTION_B, 0, 100,
										1, BackupActivity.this);
							}

						

					});
			

		} else if (v == mBtnBack) {
			finish();
		}
	}

	@Override


	public void AfOnResult(int httpHandle, int flag, int code, Object result, Object user_data) {
		int userdata=(Integer)user_data;
		if( code != Consts.REQ_CODE_SUCCESS){
			return;
		}
		switch (flag) {
		case Consts.REQ_PHONEBOOK_BACKUP:
			dismissAllDialog();
			if(userdata==0){
				// 备份
				ToastManager.getInstance().show(this, R.string.bak_succeeded);
			}
			if(userdata==1){
				AfPbInfo info = (AfPbInfo)result;
				Contacts contact =new Contacts();
				String names[] =info.name;
				String phones[]=info.phone;
				for(int i=0; i< names.length;i++){
					String name=names[i];
					contact.name=name;
				}
				List<Contacts> list = new ArrayList<Contacts>();
				list.add(contact);
				Query q = new Query(null, BackupActivity.this);
				q.saveOrUpdate(list, handler);
				ToastManager.getInstance().show(this, R.string.restore_succeeded);
			}
			
		
			}
		
		}

	

	Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			System.out.println("arg1 " + msg.arg1);
			// if (msg.arg1 != msg.what) {
			// progress.setMax(msg.what);
			// progress.setProgress(msg.arg1);
			// } else {
			// progress.cancel();
			// }
		}
	};

	@Override
	public void reqCode(int code, int flag) {
		// TODO Auto-generated method stub

	}

	private void getContacts() {
		// TODO Auto-generated method stub
		queryHandler = new Query(getContentResolver(), this);
		queryHandler.setQueryComplete(this);
		queryHandler.query();
	}


	@Override
	public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
		// TODO Auto-generated method stub
		return false;
	}


	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if(keyCode == KeyEvent.KEYCODE_BACK){
			finish();
		}
		return false;
	}
	

	@Override
	public void onComplete(Cursor c) {
		// TODO Auto-generated method stub
		List<Contacts> list = queryHandler.getContacts(this, c);
		this.list = list;
	}
}

2、点击备份按钮,
mAfCorePalmchat.AfHttpPhonebookBackup(pb_name,pb_phone, Consts.HTTP_ACTION_A, 0, 0, 0,
										BackupActivity.this);
AfHttpPhonebookBackup()
被调用。


看下这个方法
public int AfHttpPhonebookBackup(String pb_name[], String pb_phone[], byte action, int start, int limit, Object user_data, AfHttpResultListener result){
		int handle = HttpPhonebookBackUP(pb_name, pb_phone, action, start, limit, 0);
		AfAddHttpListener(handle, result, null, user_data);
		return handle;		
	}
粗体部分是一个native本地方法 private native int HttpPhonebookBackUP(String pb_name[], String pb_phone[], byte action, int start, int limit, int user_data)
这里要实现的功能就是把本地通讯录备份到云端(服务端),通过传数据(通讯录姓名组,联系电话组等)到中间件,中间件再传给服务端。
3、本地方法的实现
JNIEXPORT jint JNICALL Java_com_core_AfPalmchat_HttpPhonebookBackUP(JNIEnv *env, jobject thiz, jobjectArray pb_name, jobjectArray pb_phone,
		jbyte action, jint start, jint limit, jint user_data)
{
	AFMBOI_HTTP_HANDLE http_id = AFMBOI_HTTP_HANDLE_INVALID;
	AFMOBI_HTTP_PB_BACKUP_PARAM param = {0};
	int len1 = 0, len2 = 0, len3 = 0;
	AFMOBI_ARRAY_PTR backup[2] = {NULL, NULL};
	jobjectArray array[2] = {pb_name, pb_phone};
	CHAR* tmp;
	jstring str;

	if(HTTP_ACTION_A == action  || HTTP_ACTION_C == action)
	{
		 if( NULL == pb_name || NULL == pb_phone)
		    {
		    	return AFMBOI_HTTP_HANDLE_INVALID;
		    }
			len1 = env->GetArrayLength(pb_name);
			len2 = env->GetArrayLength(pb_phone);

			if( len1 != len2 || len1 <= 0)
			{
				return AFMBOI_HTTP_HANDLE_INVALID;
			}

			for(len3 = 0; len3 < 2; len3++)
			{
				backup[len3] = afmobi_array_new(len1, AF_FALSE);

				for(len2 = 0; len2 < len1; len2++)
				{
					str = (jstring)env->GetObjectArrayElement(array[len3], len2);
					JNI_GET_UTF_CHAR(tmp, str);
					//AFMOBI_TRACE((const INT8S*)"HttpPhonebookBackUP: tmp = %s, len3 = %d, len2 = %d", tmp, len3, len2);
					afmobi_array_append(backup[len3],(void*)tmp);
				}
			}
			param.pb_name = backup[0];
			param.pb_phone = backup[1];
	}

	param.user_data = (INT32U)user_data;
	param.action = (HTTP_ACTION_TYPE)action;
	param.start = start;
	param.limit = limit;

	http_id = afmobi_http_pb_backup(&param,(AFMOBI_ON_TASK_RESULT_CALLBACK)AfmobiOnResult);

	for(len3 = 0; len3 < 2; len3++)
	{
		for(len2 = 0; len2 < len1; len2++)
		{
			str = (jstring)env->GetObjectArrayElement(array[len3], len2);
			tmp = (CHAR*)afmobi_array_get_at(backup[len3], (INT32U)len2);
			JNI_RELEASE_STR_STR(tmp, str);
		}
		afmobi_array_delete(backup[len3], AF_NULL);
	}
	return http_id;

}
注意上面标红部分,传姓名组和电话组过来,对应本地就是jobjectarray.其他类型道理类似

这里重点工作就是
http_id = afmobi_http_pb_backup(&param,(AFMOBI_ON_TASK_RESULT_CALLBACK)AfmobiOnResult);
1、这里的参数param就包含了前面传过来的姓名组合电话号码组等
2、这里有个函数指针,当其他地方使用到这里时,该函数会被调用
这样当该方法执行并成功后,就把通讯录传到服务端了,同时向java层返回一个代表执行成功的数据。那么这个过程是如何实现的呢?
在前面的activity之间有一个重载的
public void AfOnResult(int httpHandle, int flag, int code, Object result, Object user_data)方法
这个方法被
AfOnResultInner()调用,而
AfOnResultInner这个方法则被C/C++调用


private final static void AfOnResultInner(int httpHandle, int flag, int code, Object result, int progress, int user_data) {

		Handler mainHandler = 	getMainHandle();
		if( null != mainHandler){
			HttpResponseInner response = new HttpResponseInner(httpHandle, flag, code, result, progress, user_data);
			
			Message message= Message.obtain(mainHandler, (progress < 0 ? AF_MSG_RESULT :  AF_MSG_PROGRESS), 0, 0, response);
			
			mainHandler.sendMessage(message);	
		}

	}

     @Override
        public void handleMessage(Message msg){
            switch (msg.what){
            	case AF_MSG_RESULT:
            	case AF_MSG_PROGRESS:
            	{
            		HttpResponseInner response = (HttpResponseInner)msg.obj;         		
            		HttpListenerInner listener = (HttpListenerInner)mHttpListener.get(response.httpHandle);
            		
            		if( null != listener){
            			if( msg.what == AF_MSG_RESULT){
            				if( null != listener.mResultListener){
            					listener.mResultListener.AfOnResult(response.httpHandle, response.flag, response.code, response.result, listener.mUserData);
            					AfRemoveHttpListener(response.httpHandle);
            				}            					
            			}else{
            				if(null != listener.mProgressListener){
            					listener.mProgressListener.AfOnProgress(response.httpHandle, response.flag, response.progress, listener.mUserData);	
            				}
            			}
            		}
            		            		
            	}
            	break;

}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv *env;
	jclass clazz;
	jint result = -1;
	jint i;

	AFMOBI_TRACE((const INT8S*)"JNI_OnLoad: entry\n");
	if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
	{
		AFMOBI_TRACE((const INT8S*)"ERROR: GetEnv failed\n");
		goto error;
	}

	if ((clazz = env->FindClass((const char*)JNI_GET_CLASS_NAME(JAVA_CLASS_AfCorePalmchat))) == NULL)
	{
		AFMOBI_TRACE((const INT8S*)"ERROR: FindClass com/core/AfPalmchat failed\n");
		goto error;
	}

	if (env->RegisterNatives(clazz, g_method_table, sizeof(g_method_table) / sizeof(JNINativeMethod)) < 0)
	{
		AFMOBI_TRACE((const INT8S*)"RegisterNatives failed");
        goto error;
    }

    //load global object
	for(i = 0; i < JAVA_CLASS_MAX; i++)
	{
		JNI_initClassHelper(env, (const char *)JNI_GET_CLASS_NAME(i), &JNI_GET_CLASS_OBJ(i), &JNI_GET_CLASS_CONSTRUCT_METHODE(i));
	}
	//load static method
	JNI_SET_STATIC_METHOD(JAVA_STATIC_METHOD_RESULT, env->GetStaticMethodID(clazz, "AfOnResultInner","(IIILjava/lang/Object;II)V"));
	JNI_SET_STATIC_METHOD(JAVA_STATIC_METHOD_SYS, env->GetStaticMethodID(clazz, "AfSysMsgProcInner","(ILjava/lang/Object;I)V"));
	result = JNI_VERSION_1_4;
	g_JavaVM = vm;

	JNI_nativeHandleCrashes(env);

	AFMOBI_TRACE((const INT8S*)"JNI_OnLoad: success\n");

	error:
		return result;
}



注意:
由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),这里通过
JNI_SET_STATIC_METHOD(JAVA_STATIC_METHOD_RESULT, env->GetStaticMethodID(clazz, "AfOnResultInner","(IIILjava/lang/Object;II)V"));加载静态方法,这个时候会将数据传给
 
AfOnResultInner(int httpHandle, int flag, int code, Object result, int progress, int user_data)

方法,传过来的数据是在子线程中,通过转换将数据传到主线程中,然后在主线程中调用处理AfOnResult()方法。


后面咱们再具体分析这些细节部分。。。























   

你可能感兴趣的:([置顶] jni学习之三--数据传递)