Android开发网 - 注册送白菜网http://www.teaching4real.com/android/Android开发者的加油站 Android学习者的乐园RainbowSoft Studio Z-Blog 1.8 Walle Build 100427zh-CNCopyright © 2011-2017 Android开发网. 版权所有.Tue, 24 Oct 2017 14:47:44 +0800Android手机卫士(二十二):md5加密过程a@b.com (鸡啄米)http://www.teaching4real.com/android/example/717.htmlTue, 24 Oct 2017 14:43:08 +0800http://www.jizhuomi.com/android/example/717.html  在之前的文章中,我们将用户的密码使用SharedPreferences存储,我们打开/data/data/com.wuyudong.mobilesafe/shared_prefs文件夹下的 config.xml 文件,导入到本地,查看内容:

XML/HTML代码
  1. <?xml version='1.0' encoding='utf-8' standalone='yes' ?>  
  2. <map>  
  3.     <string name="mobile_safe_psd">123</string>  
  4.     <boolean name="open_update" value="false" />  
  5. </map>  

  密码居然使用的是明文,这样是非常不安全的。这里采用md5加密

  编写Md5Util工具类,代码如下:

Java代码
  1. package com.wuyudong.mobilesafe.Utils;  
  2.   
  3. /** 
  4.  * Created by wuyudong on 2016/10/9. 
  5.  */  
  6.   
  7. import java.security.MessageDigest;  
  8. import java.security.NoSuchAlgorithmException;  
  9.   
  10. public class Md5Util {  
  11.     /** 
  12.      * 给指定字符串按照md5算法去加密 
  13.      * 
  14.      * @param psd 需要加密的密码    加盐处理 
  15.      * @return md5后的字符串 
  16.      */  
  17.     public static String encoder(String psd) {  
  18.         try {  
  19.             //加盐处理  
  20.             psd = psd + "mobilesafe";  
  21.             //1,指定加密算法类型  
  22.             MessageDigest digest = MessageDigest.getInstance("MD5");  
  23.             //2,将需要加密的字符串中转换成byte类型的数组,然后进行随机哈希过程  
  24.             byte[] bs = digest.digest(psd.getBytes());  
  25.        //3,循环遍历bs,然后让其生成32位字符串,固定写法  
  26.             //4,拼接字符串过程  
  27.             StringBuffer stringBuffer = new StringBuffer();  
  28.             for (byte b : bs) {  
  29.                 int i = b & 0xff;  
  30.                 //int类型的i需要转换成16机制字符  
  31.                 String hexString = Integer.toHexString(i);  
  32.                 if (hexString.length() < 2) {  
  33.                     hexString = "0" + hexString;  
  34.                 }  
  35.                 stringBuffer.append(hexString);  
  36.             }  
  37.             //5,打印测试  
  38.             System.out.println(stringBuffer.toString());  
  39.             return stringBuffer.toString();  
  40.         } catch (NoSuchAlgorithmException e) {  
  41.             e.printStackTrace();  
  42.         }  
  43.         return "";  
  44.     }  
  45. }  

  md5加密:将字符串转换成 32位的字符串(16进制字符(0~f)) 不可逆

  例如:123加密后:202cb962ac59075b964b07152d234b70

  接下来直接调用加密类即可,分别在“设置密码”和“确认密码”两个对话框进行加密比对,具体代码如下:

Java代码
  1. /** 
  2.  * 确认密码对话框 
  3.  */  
  4. private void showConfirmPsdDialog() {  
  5.     //需要自己去定义对话框的显示样式,所以要调用dialog.setView(view);  
  6.     Builder builder = new Builder(this);  
  7.     final AlertDialog dialog = builder.create();  
  8.     final View view = inflate(this, R.layout.dialog_confirm_psd, null);  
  9.     //让对话框显示一个自己定义的对话框界面效果  
  10.     dialog.setView(view);  
  11.     dialog.show();  
  12.   
  13.     Button bt_submit = (Button) view.findViewById(R.id.bt_submit);  
  14.     Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel);  
  15.   
  16.     bt_submit.setOnClickListener(new OnClickListener() {  
  17.         @Override  
  18.         public void onClick(View v) {  
  19.             EditText et_confirm_psd = (EditText) view.findViewById(R.id.et_confirm_psd);  
  20.             String confirmPsd = et_confirm_psd.getText().toString();  
  21.   
  22.             if (!TextUtils.isEmpty(confirmPsd)) {  
  23.                 //将存储在sp中32位的密码,获取出来,然后将输入的密码同样进行md5,然后与sp中存储密码比对  
  24.                 String psd = SpUtil.getString(getApplicationContext(), ConstantValue.MOBILE_SAFE_PSD, "");  
  25.   
  26.                 if (psd.equals(Md5Util.encoder(confirmPsd))) {  
  27.                     //进入用户手机防盗模块,开启一个新的activity  
  28.                     Intent intent = new Intent(getApplicationContext(), testActivity.class);  
  29.                     startActivity(intent);  
  30.                     //跳转到新的界面以后需要去隐藏对话框  
  31.                     dialog.dismiss();  
  32.                 } else {  
  33.                     ToastUtil.show(getApplicationContext(), "输入密码错误");  
  34.                 }  
  35.   
  36.             } else {  
  37.                 //提示用户密码输入为空的情况  
  38.                 ToastUtil.show(getApplicationContext(), "请输入密码");  
  39.             }  
  40.         }  
  41.     });  
  42.     bt_cancel.setOnClickListener(new OnClickListener() {  
  43.         @Override  
  44.         public void onClick(View view) {  
  45.             dialog.dismiss();  
  46.         }  
  47.     });  
  48. }  
  49.   
  50. /** 
  51.  * 设置密码对话框 
  52.  */  
  53. private void showSetPsdDialog() {  
  54.     //需要自己去定义对话框的显示样式,所以要调用dialog.setView(view);  
  55.     Builder builder = new Builder(this);  
  56.     final AlertDialog dialog = builder.create();  
  57.     final View view = inflate(this, R.layout.dialog_set_psd, null);  
  58.     //让对话框显示一个自己定义的对话框界面效果  
  59.     dialog.setView(view);  
  60.     dialog.show();  
  61.   
  62.     Button bt_submit = (Button) view.findViewById(R.id.bt_submit);  
  63.     Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel);  
  64.   
  65.     bt_submit.setOnClickListener(new OnClickListener() {  
  66.         @Override  
  67.         public void onClick(View v) {  
  68.             EditText et_set_psd = (EditText) view.findViewById(R.id.et_set_psd);  
  69.             EditText et_confirm_psd = (EditText) view.findViewById(R.id.et_confirm_psd);  
  70.             String psd = et_set_psd.getText().toString();  
  71.             String confirmPsd = et_confirm_psd.getText().toString();  
  72.             if (!TextUtils.isEmpty(psd) && !TextUtils.isEmpty(confirmPsd)) {  
  73.                 //进入用户手机防盗模块  
  74.                 if (psd.equals(confirmPsd)) {  
  75.                     Intent intent = new Intent(getApplicationContext(), testActivity.class);  
  76.                     startActivity(intent);  
  77.                     //跳转到新的界面以后需要去隐藏对话框  
  78.                     dialog.dismiss();  
  79.                     SpUtil.putString(getApplicationContext(), ConstantValue.MOBILE_SAFE_PSD, Md5Util.encoder(psd));  
  80.   
  81.                 } else {  
  82.                     ToastUtil.show(getApplicationContext(), "密码不一致");  
  83.                 }  
  84.   
  85.             } else {  
  86.                 //提示用户密码输入为空的情况  
  87.                 ToastUtil.show(getApplicationContext(), "请输入密码");  
  88.             }  
  89.         }  
  90.     });  
  91.     bt_cancel.setOnClickListener(new OnClickListener() {  
  92.         @Override  
  93.         public void onClick(View view) {  
  94.             dialog.dismiss();  
  95.         }  
  96.     });  
  97. }
]]>
Android开发实例http://www.teaching4real.com/android/example/717.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=717http://www.jizhuomi.com/android/cmd.asp?act=tb&id=717&key=fc84d9e9
Android Studio(二十八):CPU Monitora@b.com (鸡啄米)http://www.jizhuomi.com/android/environment/716.htmlWed, 11 Oct 2017 09:45:03 +0800http://www.jizhuomi.com/android/environment/716.html  Android Monitor包含一个CPU Monitor,可以让你非常方便的监测你的应用的CPU的使用。它显示试试的CPU使用。

  在CPU Monitor显示正在运行的应用

  1、 打开一个项目

  2、 在物理设备或虚拟机中运行应用

  3、 显示Android Monitor

  4、 点击Monitors并且显示CPU Monitor。

  如图:

Android Studio(二十八):CPU Monitor

]]>
Android开发环境http://www.teaching4real.com/android/environment/716.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=716http://www.jizhuomi.com/android/cmd.asp?act=tb&id=716&key=9bd1816b
推荐十本Android开发精选书籍a@b.com (鸡啄米)http://www.jizhuomi.com/android/book/715.htmlWed, 27 Sep 2017 09:47:43 +0800http://www.jizhuomi.com/android/book/715.html  推荐十本Android开发精选书籍推荐,包括书籍简介以及适合阅读人群:

      书籍名称:《深入理解Android内核设计思想(第2版)(上下册)》

      书籍简介:

      本书从操作系统的基础知识入手,全面剖析进程/线程、内存管理、Binder机制、GUI显示系统、多媒体管理、输入系统、虚拟机等核心技术在Android中的实现原理。书中讲述的知识点大部分来源于工程项目研发,因而具有较强的实用性,希望可以让读者“知其然,更知其所以然”。本书分为编译篇、系统原理篇、应用原理篇、系统工具篇,共4篇25章,基本涵盖了参与Android开发所需具备的知识,并通过大量图片与实例来引导读者学习,以求尽量在源码分析外为读者提供更易于理解的思维方式。本书既适合Android系统工程师,也适合于应用开发工程师来阅读,从而提升Android开发能力。读者可以在本书潜移默化的学习过程中更深刻地理解Android系统,并将所学知识自然地应用到实际开发难题的解决中。

   书籍名称:《Android Studio应用开发实战详解》

      书籍简介:

       全书共分18章,依次讲解了Android开发基础、搭建Android开发环境、Android Studio集成开发环境介绍、Android Studio常见操作、分析Android应用程序文件的组成、Gradle技术基础、UI界面布局、Material Design设计语言、核心组件介绍、Android事件处理、图形图像和动画处理、开发音频/视频应用程序、GPS地图定位、Android传感器应用开发、编写安全的应用程序、Google Now和Android Wear、Android应用优化以及Android TV开发。本书几乎涵盖了Android Studio应用开发所能涉及的所有领域,在讲解每一个知识点时,都遵循了理论联系实际的讲解方式,用具体实例彻底剖析了Android Studio开发的每一个知识点。本书讲解方法通俗易懂,特别有利于初学者学习并消化。本书适合Android初级读者、Android应用开发人员、Android爱好者、Android Studio开发人员、Android智能家居、Android可穿戴设备研发人员学习,也可以作为相关培训学校和大专院校相关专业的教学用书。

   书籍名称:《深入解析Android 虚拟机》

      书籍简介:

       Android系统从诞生到现在的短短几年时间里,凭借其易用性和开发的简洁性,赢得了广大开发者的支持。在整个Android系统中,Dalvik VM一直是贯穿从底层内核到高层应用开发的核心。本书循序渐进地讲解了Android虚拟机系统的基本知识,并剖析了其整个内存系统的进程和运作流程,并对虚拟机系统优化和异常处理的知识进行了详细讲解。本书几乎涵盖了Dalvik VM系统的所有主要内容,并且讲解方法通俗易懂,特别有利于读者学习并消化。

  书籍名称:《Android传感器开发与智能设备案例实战》

      书籍简介:

      本书主要介绍Android传感器和外设的开发,共29章,主要包括Android开发技术基础、获取并编译源码、Android技术核心框架分析、Android传感器系统分析、使用地图定位、光线传感器详解、接近警报传感器详解、磁场传感器详解、加速度传感器详解、方向传感器详解、陀螺仪传感器详解、旋转向量传感器详解、距离传感器详解、气压传感器详解、温度传感器详解、湿度传感器详解、Android蓝牙系统概述、低功耗蓝牙技术详解、语音识别技术详解、手势识别技术详解、NFC近场通信技术详解、拍照解析条形码技术详解、基于图像处理的人脸识别技术详解、行走轨迹记录器、手势音乐播放器和智能家居系统等。本书几乎涵盖了Android传感器和外设开发所需的所有主要内容,讲解方法通俗易懂。

  书籍名称:《构建安全的Android App》

      书籍简介:

      本书介绍了主流的Android安全技术开发的方法,并把此方法应用在整个Android应用开发的代码里。书中作者使用详细的例子,从成百上千个他已经亲自审核的应用程序中,帮助读者解读应用程序被攻击的原因,然后演示更安全的解决方案。书中包括身份验证、网络、数据库、服务器攻击、数据、硬件等技术,并阐明了每种技术代码示例的含义和作用,可以帮助读者达到学以致用的目标。

  书籍名称:《Android 应用测试指南》

       书籍简介:

      《Android应用测试指南》是一本移动测试实用工具书。本书针对当前流行的技术、框架和工程质量改进工具进行了介绍,一步一步清晰地指导大家如何去写应用程序的测试用例,利用各种测试手段来保证Android项目质量。本书首先介绍了TTD(Android测试驱动开发)。TTD是软件开发过程中一个敏捷模式,能让你在早期发现应用中的Bug。书中给出了一些典型的样例工程来示范测试,包括最简单的单元测试和最复杂的性能测试。另外,本书以诊断的方式来详细描述Android测试中较广泛、较流行的应用技术。对于梦想在Android测试领域启航的程序员和测试人员来说这无疑是一本非常珍贵、有用的参考书。

  书籍名称:《精通Android网络开发》

       书籍简介:

       本书详细介绍了Android网络开发的有关内容,全书共分为5篇,共计25章,从搭建Android开发环境和核心框架分析讲起,依次讲解了Android技术核心框架,网络开发技术基础,HTTP数据通信,URL处理数据,处理XML数据,下载远程数据,上传数据,使用Socket实现数据通信,使用WebKit浏览网页数据,Wi-Fi系统应用,蓝牙系统应用,邮件应用,RSS应用,网络视频处理,网络流量监控,网络RSS阅读器,开发一个邮件系统,在Android中开发移动微博应用、网络防火墙系统,开发Web版的电话本管理系统、移动微信系统等知识。本书几乎涵盖了Android网络应用中的所有主要内容,讲解方法通俗易懂。

  书籍名称:《Java和Android开发学习指南(第2版)》

       书籍简介:

       本书是Java语言学习指南,特别针对使用Java进行Android应用程序开发展开了详细介绍。全书共50章。分为两大部分。第1部分(第1章到第22章)主要介绍Java语言基础知识及其功能特性。第2部分(第23章到第50章)主要介绍如何有效地构建Android应用程序。本书适合任何想要学习Java语言的读者阅读,特别适合想要成为Android应用程序开发人员的读者学习参考。

  书籍名称:《Android开发进阶:从小工到专家》

      书籍简介:

      本书是一本专门介绍Android开发的图书。书中首先对Android开发的核心知识点进行深入讲解,然后介绍单元测试、代码规范、版本控制、重构、架构等重要的知识,使得读者在深入掌握技术的同时也帮助他们开阔眼界,且能够以更专业的方式设计应用软件,完成从只会实现功能的初级程序员到软件工程师、设计师的转变。本书的主要内容为:构成Android系统基石的四大组件、创造出丰富多彩的UI设计的控件、保证App流畅的多线程开发、必知必会的HTTP网络请求应用、数据存储的核心SQLite数据库、让程序更优秀的性能优化、让程序更整洁的代码规范、管理程序的好帮手Git版本控制,以及需要掌握的高级技术,如单元测试、六大原则与设计模式、重构和综合实战等。

   书籍名称:《Android框架揭秘》

       书籍简介:《Android框架揭秘》通过对Android系统源代码的分析,主要介绍Android框架的初始化过程及主要组件的工作原理。作者直接分析和整理了Android框架的主要源代码,并详细讲解了理解框架工作原理所需的各种基础知识和构成实际Android平台骨干的服务框架。其中的主要内容包括:通过启动程序了解Android框架的概要,移植Android以及如何开发适合各种机器的应用程序,分析Android框架所需的基础知识,JNI(Java Native Interface)与Binder基础知识,Zygote、Service Manager、Service Server等Android核心组件,Android服务框架的结构与理解,通过Camera Service、Activity Manager Service等服务分析实际的Android服务等。《Android框架揭秘》不仅可以供具备一定开发经验的Android开发人员参考阅读,也可作为Android开发初学者的Android框架博一把白菜论坛手机教材使用。

]]>
Android开发书籍http://www.teaching4real.com/android/book/715.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=715http://www.jizhuomi.com/android/cmd.asp?act=tb&id=715&key=c4f6dda4
Android手机卫士(二十一):确认密码对话框编写a@b.com (鸡啄米)http://www.jizhuomi.com/android/example/714.htmlTue, 19 Sep 2017 09:33:33 +0800http://www.jizhuomi.com/android/example/714.html    本文接着实现“确认密码”功能,也即是用户以前设置过密码,现在只需要输入确认密码

Android手机卫士(二十一):确认密码对话框编写

  布局文件和《Android 手机卫士--设置密码对话框》中的布局基本类似,所有copy一下,修改一点细节就搞定:

XML/HTML代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical">  
  6.   
  7.     <TextView  
  8.         style="@style/TitleStyle"  
  9.         android:background="#f00"  
  10.         android:text="确认密码"  
  11.         />  
  12.     <EditText  
  13.         android:id="@+id/et_confirm_psd"  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         android:hint="确认密码"  
  17.         />  
  18.   
  19.     <LinearLayout  
  20.         android:layout_width="match_parent"  
  21.         android:layout_height="wrap_content">  
  22.   
  23.         <Button  
  24.             android:id="@+id/bt_submit"  
  25.             android:layout_width="0dp"  
  26.             android:layout_height="wrap_content"  
  27.             android:layout_weight="1"  
  28.             android:text="确认" />  
  29.   
  30.         <Button  
  31.             android:id="@+id/bt_cancel"  
  32.             android:layout_width="0dp"  
  33.             android:layout_height="wrap_content"  
  34.             android:layout_weight="1"  
  35.             android:text="取消" />  
  36.     </LinearLayout>  
  37.   
  38. </LinearLayout>  

  代码逻辑也基本类似,简单的修改一下

Java代码
  1. /** 
  2.  * 确认密码对话框 
  3.  */  
  4. private void showConfirmPsdDialog() {  
  5.     //需要自己去定义对话框的显示样式,所以要调用dialog.setView(view);  
  6.     Builder builder = new Builder(this);  
  7.     final AlertDialog dialog = builder.create();  
  8.     final View view = inflate(this, R.layout.dialog_confirm_psd, null);  
  9.     //让对话框显示一个自己定义的对话框界面效果  
  10.     dialog.setView(view);  
  11.     dialog.show();  
  12.   
  13.     Button bt_submit = (Button) view.findViewById(R.id.bt_submit);  
  14.     Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel);  
  15.   
  16.     bt_submit.setOnClickListener(new OnClickListener() {  
  17.         @Override  
  18.         public void onClick(View v) {  
  19.             EditText et_confirm_psd = (EditText) view.findViewById(R.id.et_confirm_psd);  
  20.             String confirmPsd = et_confirm_psd.getText().toString();  
  21.             String psd = SpUtil.getString(getApplicationContext(),ConstantValue.MOBILE_SAFE_PSD, "");  
  22.             if(!TextUtils.isEmpty(confirmPsd)){  
  23.                 //进入用户手机防盗模块  
  24.                 if(psd.equals(confirmPsd)) {  
  25.                     Intent intent = new Intent(getApplicationContext(), testActivity.class);  
  26.                     startActivity(intent);  
  27.                     //跳转到新的界面以后需要去隐藏对话框  
  28.                     dialog.dismiss();  
  29.                 } else {  
  30.                     ToastUtil.show(getApplicationContext(),"输入密码错误");  
  31.                 }  
  32.   
  33.             }else{  
  34.                 //提示用户密码输入为空的情况  
  35.                 ToastUtil.show(getApplicationContext(),"请输入密码");  
  36.             }  
  37.         }  
  38.     });  
  39.     bt_cancel.setOnClickListener(new OnClickListener() {  
  40.         @Override  
  41.         public void onClick(View view) {  
  42.             dialog.dismiss();  
  43.         }  
  44.     });  
  45. }
]]>
Android开发实例http://www.teaching4real.com/android/example/714.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=714http://www.jizhuomi.com/android/cmd.asp?act=tb&id=714&key=ec305767
Android游戏开发实践之NDK与JNI开发04a@b.com (鸡啄米)http://www.jizhuomi.com/android/game/713.htmlWed, 13 Sep 2017 09:47:01 +0800http://www.jizhuomi.com/android/game/713.html  有了前面几篇NDK与JNI开发相关基础做铺垫,再来通过代码说明下这方面具体的操作以及一些重要的细节。那么,就继续NDK与JNI的学习总结。

  JavaVM和JNIEnv

  在jni.h头文件中定义了两种重要的数据结构JavaVM和JNIEnv,并且在C和C++中它们的实现是不同的(通过#if defined(__cplusplus)宏定义实现)。本质都是指向封装了JNI函数列表的指针。

  JavaVM

  是java虚拟机在jni层的表示。在Android中一个JVM只允许有一个JavaVM对象。可以在线程间共享一个JavaVM对象。

  JavaVM声明

  在jni中针对C语言环境和C++语言环境的JavaVM实现有所不同。

  C版的JavaVM声明为:

C++代码
  1. typedef const struct JNIInvokeInterface* JavaVM;  
  2.   
  3. struct JNIInvokeInterface {  
  4.     void*       reserved0;  
  5.     void*       reserved1;  
  6.     void*       reserved2;  
  7.   
  8.     jint        (*DestroyJavaVM)(JavaVM*);  
  9.     jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);  
  10.     jint        (*DetachCurrentThread)(JavaVM*);  
  11.     jint        (*GetEnv)(JavaVM*, void**, jint);  
  12.     jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);  
  13. };  

  C++版的JavaVM声明为:

Java代码
  1. typedef _JavaVM JavaVM;  
  2.   
  3. struct _JavaVM {  
  4.     const struct JNIInvokeInterface* functions;  
  5.   
  6. #if defined(__cplusplus)  
  7.     jint DestroyJavaVM()  
  8.     { return functions->DestroyJavaVM(this); }  
  9.     jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)  
  10.     { return functions->AttachCurrentThread(this, p_env, thr_args); }  
  11.     jint DetachCurrentThread()  
  12.     { return functions->DetachCurrentThread(this); }  
  13.     jint GetEnv(void** env, jint version)  
  14.     { return functions->GetEnv(this, env, version); }  
  15.     jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)  
  16.     { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }  
  17. #endif /*__cplusplus*/  
  18. };  

  JavaVM获取方式

  (1)jni动态注册的方式。在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* vm, void* reserved),并传入JavaVM指针:

C++代码
  1. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
  2.   
  3. }  

  (2)在本地代码中通过调用jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*)来创建。

  JNIEnv

  简单来说,就是JNIEnv提供了所有JNI函数调用的接口。不能在线程间共享同一个JNIEnv变量,仅在创建它的线程有效,如果要在其它线程访问JVM,需要调用AttachCurrentThread或AttachCurrentThreadAsDaemon将当前线程与JVM绑定。再通过JavaVM对象的GetEnv来获取JNIEnv。

  JNIEnv声明

  与JavaVM类似,JNIEnv在C和C++语言中的声明也有所不同。

  C版的JavaVM声明为:

C++代码
  1. typedef const struct JNINativeInterface* JNIEnv;  
  2.   
  3. struct JNINativeInterface {  
  4.         jint        (*GetVersion)(JNIEnv *);  
  5.         ···  
  6. }  

  C++版的JavaVM声明为:

C++代码
  1. typedef _JNIEnv JNIEnv;  
  2.   
  3. struct _JNIEnv {  
  4.     /* do not rename this; it does not seem to be entirely opaque */  
  5.     const struct JNINativeInterface* functions;  
  6.   
  7. #if defined(__cplusplus)  
  8.   
  9.     jint GetVersion()  
  10.     { return functions->GetVersion(this); }  
  11.   
  12.     ...  
  13. }  

  jobject、jclass、jmethodID和jfieldID

  jobject:

  是JNI对原始java.lang.Object的映射。可以通过调用NewObject来获得一个jobject对象。例如:

  env->NewObject(jclass clazz, jmethodID methodID, ...)

  jclass:

  是JNI对原始java.lang.Class的映射。可以通过调用FindClass来获得jclass对象。例如:

  jclass intArrayClass = env->FindClass("[I");

  jmethodID:

  获取对应类成员方法的方法id。可以通过调用GetMethodID来获取。例如:

  jmethodID myMethodId = env->(jclass clazz, const char *name, const char *sig);

  jfieldID:

  获取对应类成员变量的字段id。可以通过调用GetFieldID来获得。例如:

  jfieldID nameFieldId = env->GetFieldID(jclass clazz, const char *name, const char *sig)

  本地库调用

  JNI的加载本地库中的代码,步骤简述如下(同时,也是Android推荐的做法):

  (1)在java类的静态块中调用System.loadLibrary来加载动态库,若动态库的名字为libcocos2dx.so,那么,调用为:

Java代码
  1. static {  
  2.     System.loadLibrary("cocos2dx");  
  3. }  

  (2)在本地代码中实现JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);方法。

  (3)在该JNI_OnLoad方法中,调用env->RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods)注册所有本地的实现方法。推荐将方法声明为静态的,这样不会占据设备上的符号表的空间。

  JNI通信

  JNI的通信过程,其实就是原生Java与底层C/C++数据传递的过程。这里简单归纳下,数据传递分为以下这几种:

  • 传递基本数据类型(例如:int,float等)

  • 传递对象(例如:String,Object,自定义类MyObject等)

  • 传递数组(例如:int[], String[]等)

  • 传递集合对象(例如:ArrayList,HashMap等)

  而调用方式有可以分为:

  (1)java调用native方法

  (2)native调用java静态方法,非静态方法(成员方法),以及获取java类的成员变量。

  下面按照实现方式的不同结合以上要点,通过一个例子代码来说明下具体是如何实现的。

  (1)静态注册的方式

  工程结构如下:(这里只列举出主要说明的项)

XML/HTML代码
  1. JNISample1    
  2.   │── build.gradle  
  3.   │── CMakeLists.txt   
  4.   └── app   
  5.       ├── build.gradle  
  6.       ├── CMakeLists.txt  
  7.       └── src   
  8.           ├── cpp  
  9.           │    ├── JNIUtils.h  
  10.           │    └── JNIUtils.cpp  
  11.           └── com.alphagl.main  
  12.                     ├── JNIUtils.java  
  13.                     ├── MainActivity.Java  
  14.                     └── Person.java  

  代码如下:(这里做了下简化,去掉些注释以及单元测试部分的代码)

  MainActivity.java:

Java代码
  1. package com.alphagl.main;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.util.Log;  
  6.   
  7. public class MainActivity extends Activity {  
  8.   
  9.     static {  
  10.         System.loadLibrary("native-lib");  
  11.     }  
  12.   
  13.     protected void onCreate(Bundle savedInstanceState) {  
  14.         super.onCreate(savedInstanceState);  
  15.         setContentView(R.layout.activity_main);  
  16.   
  17.         Log.i("MainActivity""getStringFromJNI ============= " + JNIUtils.getStringFromJNI());  
  18.         Log.i("MainActivity""getIntArrayFromJNI ============= " + JNIUtils.getIntArrayFromJNI()[0] + "," + JNIUtils.getIntArrayFromJNI()[1]);  
  19.         JNIUtils.setPersonToJNI(new Person(18"jobs"));  
  20.         Log.i("MainActivity""getPersonFromJNI ============= " + JNIUtils.getPersonFromJNI().getAge()+ "," + JNIUtils.getPersonFromJNI().getName());  
  21.     }  
  22. }  

  Person.java:(封装的自定义对象)

Java代码
  1. package com.alphagl.main;  
  2.   
  3. import android.util.Log;  
  4.   
  5. public class Person {  
  6.     private int age;  
  7.     private String name;  
  8.   
  9.     public Person(int age, String name) {  
  10.         this.age = age;  
  11.         this.name = name;  
  12.     }  
  13.   
  14.     public void setAge(int age) {  
  15.         this.age = age;  
  16.     }  
  17.   
  18.     public int getAge() {  
  19.         return age;  
  20.     }  
  21.   
  22.     public void setName(String name) {  
  23.         this.name = name;  
  24.     }  
  25.   
  26.     public String getName() {  
  27.         return name;  
  28.     }  
  29.   
  30.     public void printPerson() {  
  31.         Log.d("MainActivity""age ======== " + age + "," + "name ======== " + name);  
  32.     }  
  33. }  

  JNIUtils.java:

Java代码
  1. package com.alphagl.main;  
  2.   
  3. public class JNIUtils {  
  4.     public static native String getStringFromJNI();  
  5.     public static native int[] getIntArrayFromJNI();  
  6.     public static native void setPersonToJNI(Person person);  
  7.     public static native Person getPersonFromJNI();  
  8. }  

  JNIUtils.h:

C++代码
  1. #include <jni.h>  
  2. #include <stdio.h>  
  3.   
  4. #ifndef _Included_com_alphagl_main_JNIUtils  
  5. #define _Included_com_alphagl_main_JNIUtils  
  6. #ifdef __cplusplus  
  7. extern "C" {  
  8. #endif  
  9.   
  10. JNIEXPORT jstring JNICALL Java_com_alphagl_main_JNIUtils_getStringFromJNI  
  11.   (JNIEnv *, jclass);  
  12.   
  13.   
  14. JNIEXPORT jintArray JNICALL Java_com_alphagl_main_JNIUtils_getIntArrayFromJNI  
  15.   (JNIEnv *, jclass);  
  16.   
  17.   
  18. JNIEXPORT void JNICALL Java_com_alphagl_main_JNIUtils_setPersonToJNI  
  19.   (JNIEnv *, jclass, jobject);  
  20.   
  21.   
  22. JNIEXPORT jobject JNICALL Java_com_alphagl_main_JNIUtils_getPersonFromJNI  
  23.   (JNIEnv *, jclass);  
  24.   
  25. #ifdef __cplusplus  
  26. }  
  27. #endif  
  28. #endif  

  JNIUtils.cpp

C++代码
  1. #include "JNIUtils.h"  
  2. #include <android/log.h>  
  3.   
  4. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MainActivity", __VA_ARGS__)  
  5. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "MainActivity", __VA_ARGS__)  
  6. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROE, "MainActivity", __VA_ARGS__)  
  7.   
  8.   
  9. JNIEXPORT jstring JNICALL Java_com_alphagl_main_JNIUtils_getStringFromJNI (JNIEnv *env, jclass jcls) {  
  10.     LOGD(" ====================== getStringFromJNI");  
  11.     // 构造一个String字符串  
  12.     return env->NewStringUTF("Hello from jni");  
  13. }  
  14.   
  15.   
  16. JNIEXPORT jintArray JNICALL Java_com_alphagl_main_JNIUtils_getIntArrayFromJNI (JNIEnv *env, jclass jcls) {  
  17.     LOGD(" ====================== getIntArrayFromJNI");  
  18.     // 构造一个int[]数组  
  19.     jintArray intArray = env->NewIntArray(2);  
  20.     int size[]={640, 960};  
  21.     // 给int[]数组赋值  
  22.     env->SetIntArrayRegion(intArray, 0, 2, size);  
  23.   
  24.     return intArray;  
  25. }  
  26.   
  27.   
  28. JNIEXPORT void JNICALL Java_com_alphagl_main_JNIUtils_setPersonToJNI (JNIEnv *env, jclass jcls, jobject jobj) {  
  29.     LOGD(" ====================== setPersonToJNI");  
  30.     jclass jperson = env->GetObjectClass(jobj);  
  31.     if (jperson != NULL) {  
  32.         // 获取Person对象的age字段id  
  33.         jfieldID ageFieldId = env->GetFieldID(jperson, "age""I");  
  34.         // 获取Person对象的name字段id  
  35.         jfieldID nameFieldId = env->GetFieldID(jperson, "name""Ljava/lang/String;");  
  36.   
  37.         // 获取Person的age成员变量  
  38.         jint age = env->GetIntField(jobj, ageFieldId);  
  39.         // 获取Person的name成员变量  
  40.         jstring name = (jstring)env->GetObjectField(jobj, nameFieldId);  
  41.   
  42.         const char *c_name = env->GetStringUTFChars(name, NULL);  
  43.   
  44.         // 打印从Java传递过来的Person对象的age和name变量  
  45.         LOGD("age ===== %d, name ===== %s", age, c_name);  
  46.     }  
  47.   
  48.     // 以下是从JNI构造Java对象,并调用Java类中的成员方法,仅用作演示  
  49.     // 获取Person对象的class  
  50.     jclass jstu = env->FindClass("com/alphagl/main/Person");  
  51.     // 获取Person对象的构造方法的方法id  
  52.     jmethodID personMethodId = env->GetMethodID(jperson, "<init>""(ILjava/lang/String;)V");  
  53.     // 构造一个String字符串  
  54.     jstring name = env->NewStringUTF("bill");  
  55.   
  56.     // 构造一个Person对象  
  57.     jobject  jPersonObj = env->NewObject(jstu, personMethodId, 30, name);  
  58.     // 获取Person对象的printPerson成员方法的方法id  
  59.     jmethodID jid = env->GetMethodID(jstu, "printPerson""()V");  
  60.     // 调用java的printPerson方法  
  61.     env->CallVoidMethod(jPersonObj, jid);  
  62. }  
  63.   
  64.   
  65. JNIEXPORT jobject JNICALL Java_com_alphagl_main_JNIUtils_getPersonFromJNI(JNIEnv *env, jclass jcls) {  
  66.     LOGD(" ====================== getPersonFromJNI");  
  67.     // 获取Person对象的class  
  68.     jclass jstudent = env->FindClass("com/alphagl/main/Person");  
  69.     // 获取Person对象的构造方法的方法id  
  70.     jmethodID studentMethodId = env->GetMethodID(jstudent, "<init>""(ILjava/lang/String;)V");  
  71.     // 构造一个String字符串  
  72.     jstring name = env->NewStringUTF("john");  
  73.     // 构造一个Person对象  
  74.     jobject  jstudentObj = env->NewObject(jstudent, studentMethodId, 20, name);  
  75.   
  76.     return jstudentObj;  
  77. }  

  这里再提一下,如上`JNIUtils.java`类中定义好了native方法,如何根据对象的方法签名生成对应的C/C++方法的声明。这部分内容在Android游戏开发实践(1)之NDK与JNI开发01 已经提到过,我们可以借助javah来根据编译后的.class生成对于的头文件。

  普通做法是:

Android游戏开发实践之NDK与JNI开发04

  在AndroidStudio中可以:

  Tools-> External Tools -> 添加

Android游戏开发实践之NDK与JNI开发04

  (1)javah所在的路径

  (2)命令行参数

  (3)头文件生成的路径

Android游戏开发实践之NDK与JNI开发04

  在声明了native方法的类,右键执行javah即可。

  (2)动态注册的方式

  工程结构如下:(这里只列举出主要说明的项)

XML/HTML代码
  1. JNISample2    
  2.   │── build.gradle  
  3.   │── CMakeLists.txt   
  4.   └── app   
  5.       ├── build.gradle  
  6.       ├── CMakeLists.txt  
  7.       └── src   
  8.           ├── cpp  
  9.           │   └── JNIUtils.cpp  
  10.           │      
  11.           └── com.alphagl.main  
  12.                     ├── JNIUtils.java  
  13.                     ├── MainActivity.Java  
  14.                     └── Person.java  

  这里主要看下不同的代码部分,即JNIUtils.cpp。

  JNIUtils.cpp:

C++代码
  1. #include <jni.h>  
  2. #include <string>  
  3. #include <android/log.h>  
  4.   
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MainActivity", __VA_ARGS__)  
  6. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "MainActivity", __VA_ARGS__)  
  7. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROE, "MainActivity", __VA_ARGS__)  
  8.   
  9. #define CLASSNAME "com/alphagl/main/JNIUtils"  
  10.   
  11. static jstring getStringFromJNI_native(JNIEnv *env, jclass jcls) {  
  12.     LOGD(" ====================== getStringFromJNI");  
  13.     // 构造一个String字符串  
  14.     return env->NewStringUTF("Hello from jni");  
  15. }  
  16.   
  17. static jarray getIntArrayFromJNI_native(JNIEnv *env, jclass jcls) {  
  18.     LOGD(" ====================== getIntArrayFromJNI");  
  19.     // 构造一个int[]数组  
  20.     jintArray intArray = env->NewIntArray(2);  
  21.     int size[]={640, 960};  
  22.     // 给int[]数组赋值  
  23.     env->SetIntArrayRegion(intArray, 0, 2, size);  
  24.   
  25.     return intArray;  
  26. }  
  27.   
  28. static void setJniPerson_native(JNIEnv *env, jclass jcls, jobject jobj) {  
  29.     LOGD(" ====================== setPersonToJNI");  
  30.     jclass jperson = env->GetObjectClass(jobj);  
  31.     if (jperson != NULL) {  
  32.         // 获取Person对象的age字段id  
  33.         jfieldID ageFieldId = env->GetFieldID(jperson, "age""I");  
  34.         // 获取Person对象的name字段id  
  35.         jfieldID nameFieldId = env->GetFieldID(jperson, "name""Ljava/lang/String;");  
  36.   
  37.         // 获取Person的age成员变量  
  38.         jint age = env->GetIntField(jobj, ageFieldId);  
  39.         // 获取Person的name成员变量  
  40.         jstring name = (jstring)env->GetObjectField(jobj, nameFieldId);  
  41.   
  42.         const char *c_name = env->GetStringUTFChars(name, NULL);  
  43.   
  44.         // 打印从Java传递过来的Person对象的age和name变量  
  45.         LOGD("age ===== %d, name ===== %s", age, c_name);  
  46.     }  
  47.   
  48.     // 以下是从JNI构造Java对象,并调用Java类中的成员方法,仅用作演示  
  49.     // 获取Person对象的class  
  50.     jclass jstu = env->FindClass("com/alphagl/main/Person");  
  51.     // 获取Person对象的构造方法的方法id  
  52.     jmethodID personMethodId = env->GetMethodID(jperson, "<init>""(ILjava/lang/String;)V");  
  53.     // 构造一个String字符串  
  54.     jstring name = env->NewStringUTF("bill");  
  55.   
  56.     // 构造一个Person对象  
  57.     jobject  jPersonObj = env->NewObject(jstu, personMethodId, 30, name);  
  58.     // 获取Person对象的printPerson成员方法的方法id  
  59.     jmethodID jid = env->GetMethodID(jstu, "printPerson""()V");  
  60.     // 调用java的printPerson方法  
  61.     env->CallVoidMethod(jPersonObj, jid);  
  62. }  
  63.   
  64. static jobject getJniPerson_native(JNIEnv *env, jclass jcls) {  
  65.     LOGD(" ====================== getPersonFromJNI");  
  66.     // 获取Person对象的class  
  67.     jclass jstudent = env->FindClass("com/alphagl/main/Person");  
  68.     // 获取Person对象的构造方法的方法id  
  69.     jmethodID studentMethodId = env->GetMethodID(jstudent, "<init>""(ILjava/lang/String;)V");  
  70.     // 构造一个String字符串  
  71.     jstring name = env->NewStringUTF("john");  
  72.     // 构造一个Person对象  
  73.     jobject  jstudentObj = env->NewObject(jstudent, studentMethodId, 20, name);  
  74.   
  75.     return jstudentObj;  
  76. }  
  77.   
  78. static JNINativeMethod gMethods[] = {  
  79.         {"getStringFromJNI""()Ljava/lang/String;", (void*)getStringFromJNI_native},  
  80.         {"getIntArrayFromJNI""()[I", (void*)getIntArrayFromJNI_native},  
  81.         {"setPersonToJNI""(Lcom/alphagl/main/Person;)V", (void*)setJniPerson_native},  
  82.         {"getPersonFromJNI""()Lcom/alphagl/main/Person;", (void*)getJniPerson_native}  
  83. };  
  84.   
  85. static jint registerNativeMethods(JNIEnv *env, const char* className, JNINativeMethod *gMethods, int numMethods) {  
  86.     jclass jcls;  
  87.     jcls = env->FindClass(className);  
  88.     if (jcls == NULL) {  
  89.         return JNI_FALSE;  
  90.     }  
  91.   
  92.     if (env->RegisterNatives(jcls, gMethods, numMethods) < 0) {  
  93.         return JNI_FALSE;  
  94.     }  
  95.   
  96.     return JNI_TRUE;  
  97. }  
  98.   
  99. static jint registerNative(JNIEnv *env) {  
  100.     return registerNativeMethods(env, CLASSNAME, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));  
  101. }  
  102.   
  103. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
  104.     JNIEnv *env = NULL;  
  105.     if ((vm->GetEnv((void**)&env, JNI_VERSION_1_6)) != JNI_OK) {  
  106.         return JNI_ERR;  
  107.     }  
  108.   
  109.     if (!registerNative(env)) {  
  110.         return JNI_ERR;  
  111.     }  
  112.   
  113.     return JNI_VERSION_1_6;  
  114. }  

  最后的执行结果为:

Android游戏开发实践之NDK与JNI开发04

  两种实现方式比较:

  (1)动态注册中,可以不用声明形如Java_packageName_className_methodName格式的方法。

  (2)动态注册中,要重写JNI_OnLoad方法,手动调用RegisterNatives来注册本地方法,以及声明在JNINativeMethod中。

  (3)动态注册,明显这种方式更灵活,但对代码要求更高,推荐使用这种方式。

  以上示例代码都已上传Github,有需要的可以自行查看。

  https://github.com/cnsuperx/android-jni-example

  JNI调试

  如果安装了LLVM环境的话,直接将Jni Debuggable选项打开即可。环境搭建可以参考Android游戏开发实践(1)之NDK与JNI开发03。

Android游戏开发实践之NDK与JNI开发04

  接着直接在C或C++代码中设置断点即可。

]]>
Android游戏开发http://www.teaching4real.com/android/game/713.html#commenthttp://www.teaching4real.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=713http://www.jizhuomi.com/android/cmd.asp?act=tb&id=713&key=cb9760bf
Android启动篇 — init原理(二)a@b.com (鸡啄米)http://www.jizhuomi.com/android/course/712.htmlMon, 11 Sep 2017 08:54:39 +0800http://www.jizhuomi.com/android/course/712.html  Android启动篇 — init原理(一)中讲解分init进程分析init创建系统目录并挂在相应系统文件、初始化属性域、设置系统属性、启动配置属性服务端等一系列复杂工作,很多工作和知识点跟Linux关系很大,所以没有作过多介绍,而本此对于init.rc的解析则是重中之重,所以单独拿出来进行详细分析。

Java代码
  1. int main(int argc, char** argv) {  
  2.     /* 01. 创建文件系统目录并挂载相关的文件系统 */  
  3.     /* 02. 屏蔽标准的输入输出/初始化内核log系统 */  
  4.     /* 03. 初始化属性域 */  
  5.     /* 04. 完成SELinux相关工作 */•  
  6.     /* 05. 重新设置属性 */  
  7.     /* 06. 创建epoll句柄 */  
  8.     /* 07. 装载子进程信号处理器 */  
  9.     /* 08. 设置默认系统属性 */  
  10.     /* 09. 启动配置属性的服务端 */  
  11.     /* 10. 匹配命令和函数之间的对应关系 */  
  12. -------------------------------------------------------------------------------------------   // Android启动篇 — init原理(一)中讲解  
  13.     /* 11. 解析init.rc */  
  14.     Parser& parser = Parser::GetInstance();       // 构造解析文件用的parser对象  
  15.     // 增加ServiceParser为一个section,对应name为service  
  16.     parser.AddSectionParser("service",std::make_unique<ServiceParser>());  
  17.     // 增加ActionParser为一个section,对应name为action  
  18.     parser.AddSectionParser("on", std::make_unique<ActionParser>());  
  19.     // 增加ImportParser为一个section,对应name为service  
  20.     parser.AddSectionParser("import", std::make_unique<ImportParser>());  
  21.     parser.ParseConfig("/init.rc");      // 开始实际的解析过程  

  【正文】

  init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,主要包含五种类型语句:Action、Command、Service、Option和Import,在分析代码的过程中我们会详细介绍。

  init.rc的配置代码在:system/core/rootdir/init.rc 中

  init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操作。

  init.rc文件大致分为两大部分,一部分是以“on”关键字开头的动作列表(action list):

XML/HTML代码
  1. on early-init      // Action类型语句  
  2.     # Set init and its forked children's oom_adj.     // #:注释符号  
  3.     write /proc/1/oom_score_adj -1000  
  4.     ... ...  
  5.     start ueventd  

  Action类型语句格式:

XML/HTML代码
  1. on <trigger> [&& <trigger>]*     // 设置触发器    
  2.    <command>    
  3.    <command>      // 动作触发之后要执行的命令  

  另一部分是以“service”关键字开头的服务列表(service list):  如 Zygote

XML/HTML代码
  1. service ueventd /sbin/ueventd  
  2.     class core  
  3.     critical  
  4.     seclabel u:r:ueventd:s0  

  Service类型语句格式:

XML/HTML代码
  1. service <name> <pathname> [ <argument> ]*   // <service的名字><执行程序路径><传递参数>    
  2.    <option>       // option是service的修饰词,影响什么时候、如何启动services    
  3.    <option>    
  4.    ...  

  借助系统环境变量或Linux命令,动作列表用于创建所需目录,以及为某些特定文件指定权限,而服务列表用来记录init进程需要启动的一些子进程。如上面代码所示,service关键字后的第一个字符串表示服务(子进程)的名称,第二个字符串表示服务的执行路径。

  值得一提的是在Android 7.0中对init.rc文件进行了拆分,每个服务一个rc文件。我们要分析的zygote服务的启动脚本则在init.zygoteXX.rc中定义。

  在init.rc的import段我们看到如下代码:

XML/HTML代码
  1. import /init.${ro.zygote}.rc     // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件  

  说明:

  从android5.0开始,android开始支持64位的编译,zygote本身也就有了32位和64位的区别,所以在这里用ro.zygote属性来控制启动不同版本的zygote进程。

  init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。分别是Init.zygote32.rc,Init.zygote32_64.rc,Init.zygote64.rc,Init.zygote64_32.rc,由硬件决定调用哪个文件。

  这里拿32位处理器为例,init.zygote32.rc的代码如下所示:

XML/HTML代码
  1. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  
  2.     class main         # class是一个option,指定zygote服务的类型为main  
  3.     socket zygote stream 660 root system          # socket关键字表示一个option,创建一个名为dev/socket/zygote,类型为stream,权限为660的socket  
  4.     onrestart write /sys/android_power/request_state wake          # onrestart是一个option,说明在zygote重启时需要执行的command  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart audioserver  
  7.     onrestart restart cameraserver  
  8.     onrestart restart media  
  9.     onrestart restart netd  
  10.     writepid /dev/cpuset/foreground/tasks  

  “service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server”

  在Init.zygote32.rc中,定义了一个zygote服务:zygote,由关键字service告诉init进程创建一个名为zygote的进程,这个进程要执行的程序是:/system/bin/app_process,给这个进程四个参数:

  · -Xzygote:该参数将作为虚拟机启动时所需的参数

  · /system/bin:代表虚拟机程序所在目录

  · --zygote:指明以ZygoteInit.java类中的main函数作为虚拟机执行入口

  · --start-system-server:告诉Zygote进程启动SystemServer进程

  接下来,我们回到源码当中,继续分析main函数:

Java代码
  1. /* 11. 解析init.rc */  
  2. Parser& parser = Parser::GetInstance();       // 构造解析文件用的parser对象  
  3. // 增加ServiceParser为一个section,对应name为service  
  4. parser.AddSectionParser("service",std::make_unique<ServiceParser>());  
  5. // 增加ActionParser为一个section,对应name为action  
  6. parser.AddSectionParser("on", std::make_unique<ActionParser>());  
  7. // 增加ImportParser为一个section,对应name为service  
  8. parser.AddSectionParser("import", std::make_unique<ImportParser>());  
  9. parser.ParseConfig("/init.rc");      // 开始实际的解析过程

  说明:

  上面在解析init.rc文件时使用了Parser类(在init目录下的init_parser.h中定义), 初始化ServiceParser用来解析 “service”块,ActionParser用来解析"on"块,ImportParser用来解析“import”块,“import”是用来引入一个init配置文件,来扩展当前配置的。

  /system/core/init/readme.txt 中对init文件中的所有关键字做了介绍,主要包含了Actions, Commands, Services, Options, and Imports等,可自行学习解读。

  分析init.rc的解析过程:函数定义于system/core/init/ init_parser.cpp中

Java代码
  1. bool Parser::ParseConfig(const std::string& path) {  
  2.     if (is_dir(path.c_str())) {           // 判断传入参数是否为目录地址  
  3.         return ParseConfigDir(path);      // 递归目录,最终还是靠ParseConfigFile来解析实际的文件  
  4.     }  
  5.     return ParseConfigFile(path);         // 传入传输为文件地址  
  6. }  

  继续分析ParseConfigFile():

Java代码
  1. bool Parser::ParseConfigFile(const std::string& path) {  
  2.     ... ...  
  3.     Timer t;  
  4.     std::string data;  
  5.     if (!read_file(path.c_str(), &data)) {       // 读取路径指定文件中的内容,保存为字符串形式  
  6.         return false;  
  7. }  
  8. ... ...  
  9.     ParseData(path, data);        // 解析获取的字符串  
  10.     ... ...  
  11. }  

  跟踪ParseData():

Java代码
  1. void Parser::ParseData(const std::string& filename, const std::string& data) {  
  2.     ... ...  
  3.     parse_state state;  
  4.     ... ...  
  5.     std::vector<std::string> args;  
  6.   
  7.     for (;;) {  
  8.         switch (next_token(&state)) {    // next_token以行为单位分割参数传递过来的字符串,最先走到T_TEXT分支  
  9.         case T_EOF:  
  10.             if (section_parser) {  
  11.                 section_parser->EndSection();    // 解析结束  
  12.             }  
  13.             return;  
  14.         case T_NEWLINE:  
  15.             state.line++;  
  16.             if (args.empty()) {  
  17.                 break;  
  18.             }  
  19.             // 在前文创建parser时,我们为service,on,import定义了对应的parser   
  20.             // 这里就是根据第一个参数,判断是否有对应的parser  
  21.             if (section_parsers_.count(args[0])) {  
  22.                 if (section_parser) {  
  23.                     // 结束上一个parser的工作,将构造出的对象加入到对应的service_list与action_list中  
  24.                     section_parser->EndSection();  
  25.                 }  
  26.                 // 获取参数对应的parser  
  27.                 section_parser = section_parsers_[args[0]].get();  
  28.                 std::string ret_err;  
  29.                 // 调用实际parser的ParseSection函数  
  30.                 if (!section_parser->ParseSection(args, &ret_err)) {  
  31.                     parse_error(&state, "%s\n", ret_err.c_str());  
  32.                     section_parser = nullptr;  
  33.                 }  
  34.             } else if (section_parser) {  
  35.                 std::string ret_err;  
  36.                 // 如果第一个参数不是service,on,import  
  37.                 // 则调用前一个parser的ParseLineSection函数  
  38.                 // 这里相当于解析一个参数块的子项  
  39.                 if (!section_parser->ParseLineSection(args, state.filename,   
  40.                                                              state.line, &ret_err)) {  
  41.                     parse_error(&state, "%s\n", ret_err.c_str());  
  42.                 }  
  43.             }  
  44.             args.clear();       // 清空本次解析的数据  
  45.             break;  
  46.         case T_TEXT:  
  47.             args.emplace_back(state.text);     //将本次解析的内容写入到args中  
  48.             break;  
  49.         }  
  50.     }  
  51. }  

  至此,init.rc解析完,接下来init会执行几个重要的阶段:

Java代码
  1. int main(int argc, char** argv) {  
  2.     /* 01. 创建文件系统目录并挂载相关的文件系统 */  
  3.     /* 02. 屏蔽标准的输入输出/初始化内核log系统 */  
  4.     /* 03. 初始化属性域 */  
  5.     /* 04. 完成SELinux相关工作 */•  
  6.     /* 05. 重新设置属性 */  
  7.     /* 06. 创建epoll句柄 */  
  8.     /* 07. 装载子进程信号处理器 */  
  9.     /* 08. 设置默认系统属性 */  
  10.     /* 09. 启动配置属性的服务端 */  
  11.     /* 10. 匹配命令和函数之间的对应关系 */  
  12.     /* 11. 解析init.rc*/  
  13. ----------------------------------------------------------------------------  
  14.   /* 12.  向执行队列中添加其他action */  
  15.     // 获取ActionManager对象,需要通过am对命令执行顺序进行控制  
  16.     ActionManager& am = ActionManager::GetInstance();  
  17.     // init执行命令触发器主要分为early-init,init,late-init,boot等  
  18.     am.QueueEventTrigger("early-init");    // 添加触发器early-init,执行on early-init内容  
  19.   
  20.     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...  
  21.     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  22.     // ... so that we can start queuing up actions that require stuff from /dev.  
  23.     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  24.     am.QueueBuiltinAction(keychord_init_action, "keychord_init");  
  25.     am.QueueBuiltinAction(console_init_action, "console_init");  
  26.   
  27.     // Trigger all the boot actions to get us started.  
  28.     am.QueueEventTrigger("init");        // 添加触发器init,执行on init内容,主要包括创建/挂在一些目录,以及symlink等  
  29.   
  30.     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random  
  31.     // wasn't ready immediately after wait_for_coldboot_done  
  32.     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  33.   
  34.     // Don't mount filesystems or start core system services in charger mode.  
  35.     if (bootmode == "charger") {  
  36.     am.QueueEventTrigger("charger");     // on charger阶段  
  37.     } else if (strncmp(bootmode.c_str(), "ffbm"4) == 0) {  
  38.     NOTICE("Booting into ffbm mode\n");  
  39.     am.QueueEventTrigger("ffbm");  
  40.     } else {  
  41.     am.QueueEventTrigger("late-init");          // 非充电模式添加触发器last-init  
  42.     }  
  43.   
  44.     // Run all property triggers based on current state of the properties.  
  45.     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");  

  在last-init最后阶段有如下代码:

XML/HTML代码
  1. # Mount filesystems and start core system services.  
  2. on late-init  
  3.     trigger early-fs  
  4.   
  5.     # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter  
  6.     # '--early' can be specified to skip entries with 'latemount'.  
  7.     # /system and /vendor must be mounted by the end of the fs stage,  
  8.     # while /data is optional.  
  9.     trigger fs  
  10.     trigger post-fs  
  11.   
  12.     # Load properties from /system/ + /factory after fs mount. Place  
  13.     # this in another action so that the load will be scheduled after the prior  
  14.     # issued fs triggers have completed.  
  15.     trigger load_system_props_action  
  16.   
  17.     # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter  
  18.     # to only mount entries with 'latemount'. This is needed if '--early' is  
  19.     # specified in the previous mount_all command on the fs stage.  
  20.     # With /system mounted and properties form /system + /factory available,  
  21.     # some services can be started.  
  22.     trigger late-fs  
  23.   
  24.     # Now we can mount /data. File encryption requires keymaster to decrypt  
  25.     # /data, which in turn can only be loaded when system properties are present.  
  26.     trigger post-fs-data  
  27.   
  28.     # Load persist properties and override properties (if enabled) from /data.  
  29.     trigger load_persist_props_action  
  30.   
  31.     # Remove a file to wake up anything waiting for firmware.  
  32.     trigger firmware_mounts_complete  
  33.   
  34.     trigger early-boot  
  35.    trigger boot  

  可见出发了on early-boot和on boot两个Action。

  我们看一下on boot:

XML/HTML代码
  1. on boot  
  2.     # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.     ... ...  
  7.     class_start core  

  在on boot 的最后class_start core 会启动class为core的服务,这些服务包括ueventd、logd、healthd、adbd(disabled)、lmkd(LowMemoryKiller)、servicemanager、vold、debuggerd、surfaceflinger、bootanim(disabled)等。

  回到主题,分析trigger触发器的代码,QueueEventTrigger():位于system/core/init/action.cpp

Java代码
  1. void ActionManager::QueueEventTrigger(const std::string& trigger) {  
  2.     trigger_queue_.push(std::make_unique<EventTrigger>(trigger));  
  3. }  

  此处QueueEventTrigger函数就是利用参数构造EventTrigger,然后加入到trigger_queue_中。后续init进程处理trigger事件时,将会触发相应的操作。

  再看一下QueueBuiltinAction()函数:同样位于system/core/init/action.cpp

Java代码
  1. void ActionManager::QueueBuiltinAction(BuiltinFunction func,  
  2.                                    const std::string& name) {  
  3.     // 创建action  
  4.     auto action = std::make_unique<Action>(true);  
  5.     std::vector<std::string> name_vector{name};  
  6.   
  7.     // 保证唯一性  
  8.     if (!action->InitSingleTrigger(name)) {  
  9.         return;  
  10.     }  
  11.   
  12.     // 创建action的cmd,指定执行函数和参数  
  13.     action->AddCommand(func, name_vector);  
  14.   
  15.     trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));  
  16.     actions_.emplace_back(std::move(action));  
  17. }  

  QueueBuiltinAction函数中构造新的action加入到actions_中,第一个参数作为新建action携带cmd的执行函数;第二个参数既作为action的trigger name,也作为action携带cmd的参数。

  接下来继续分析main函数:

Java代码
  1. int main(int argc, char** argv) {  
  2.     /* 01. 创建文件系统目录并挂载相关的文件系统 */  
  3.     /* 02. 屏蔽标准的输入输出/初始化内核log系统 */  
  4.     /* 03. 初始化属性域 */  
  5.     /* 04. 完成SELinux相关工作 */•  
  6.     /* 05. 重新设置属性 */  
  7.     /* 06. 创建epoll句柄 */  
  8.     /* 07. 装载子进程信号处理器 */  
  9.     /* 08. 设置默认系统属性 */  
  10.     /* 09. 启动配置属性的服务端 */  
  11.     /* 10. 匹配命令和函数之间的对应关系 */  
  12.     /* 11. 解析init.rc*/  
  13.     /* 12. 向执行队列中添加其他action */  
  14. -------------------------------------------------------------------  
  15.     /* 13. 处理添加到运行队列的事件 */  
  16.     while (true) {  
  17.     // 判断是否有事件需要处理  
  18.         if (!waiting_for_exec) {  
  19.             // 依次执行每个action中携带command对应的执行函数  
  20.      am.ExecuteOneCommand();  
  21.         // 重启一些挂掉的进程  
  22.             restart_processes();  
  23.         }  
  24.   
  25.         // 以下决定timeout的时间,将影响while循环的间隔  
  26.         int timeout = -1;  
  27.         // 有进程需要重启时,等待该进程重启  
  28.         if (process_needs_restart) {  
  29.             timeout = (process_needs_restart - gettime()) * 1000;  
  30.             if (timeout < 0)  
  31.                 timeout = 0;  
  32.         }  
  33.   
  34.         // 有action待处理,不等待  
  35.         if (am.HasMoreCommands()) {  
  36.             timeout = 0;  
  37.         }  
  38.   
  39.         // bootchart_sample应该是进行性能数据采样  
  40.         bootchart_sample(&timeout);  
  41.   
  42.         epoll_event ev;  
  43.         // 没有事件到来的话,最多阻塞timeout时间  
  44.         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));  
  45.         if (nr == -1) {  
  46.             ERROR("epoll_wait failed: %s\n", strerror(errno));  
  47.         } else if (nr == 1) {  
  48.             //有事件到来,执行对应处理函数  
  49.             //根据上文知道,epoll句柄(即epoll_fd)主要监听子进程结束,及其它进程设置系统属性的请求  
  50.             ((void (*)()) ev.data.ptr)();  
  51.         }  
  52.     }  
  53.     return 0;  
  54. // end main  

  看一下ExecuteOneComand()函数:同样位于system/core/init/action.cpp

Java代码
  1. void ActionManager::ExecuteOneCommand() {  
  2.     // Loop through the trigger queue until we have an action to execute  
  3.     // 当前的可执行action队列为空, trigger_queue_队列不为空  
  4.     while (current_executing_actions_.empty() && !trigger_queue_.empty()) {  
  5.     // 循环遍历action_队列,包含了所有需要执行的命令,解析init.rc获得  
  6.         for (const auto& action : actions_) {  
  7.             // 获取队头的trigger, 检查actions_列表中的action的trigger,对比是否相同  
  8.             if (trigger_queue_.front()->CheckTriggers(*action)) {  
  9.                 // 将所有具有同一trigger的action加入当前可执行action队列  
  10.                 current_executing_actions_.emplace(action.get());  
  11.             }  
  12.         }  
  13.         // 将队头trigger出栈  
  14.         trigger_queue_.pop();  
  15.     }  
  16.   
  17.     if (current_executing_actions_.empty()) {   // 当前可执行的actions队列为空就返回  
  18.         return;  
  19.     }  
  20.   
  21.     auto action = current_executing_actions_.front(); // 获取当前可执行actions队列的首个action  
  22.   
  23.     if (current_command_ == 0) {  
  24.         std::string trigger_name = action->BuildTriggersString();  
  25.         INFO("processing action (%s)\n", trigger_name.c_str());  
  26.     }  
  27.   
  28.     action->ExecuteOneCommand(current_command_);     // 执行当前的命令  
  29.   
  30.     // If this was the last command in the current action, then remove  
  31.     // the action from the executing list.  
  32.     // If this action was oneshot, then also remove it from actions_.  
  33.     ++current_command_;      // 不断叠加,将action_中的所有命令取出  
  34.     if (current_command_ == action->NumCommands()) {  
  35.         current_executing_actions_.pop();  
  36.         current_command_ = 0;  
  37.         if (action->oneshot()) {  
  38.             auto eraser = [&action] (std::unique_ptr<Action>& a) {  
  39.                 return a.get() == action;  
  40.             };  
  41.             actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));  
  42.         }  
  43.     }  
  44. }  

  我们来观察一下init.rc的开头部分:

Java代码
  1. import /init.environ.rc  
  2. import /init.usb.rc  
  3. import /init.${ro.hardware}.rc  
  4. import /init.usb.configfs.rc  
  5. import /init.${ro.zygote}.rc      // 后面我们即将重点分析zygote进程  

  通过ro.zygote的属性import对应的zygote的rc文件。

Android启动篇 — init原理(二)

  我们查看init.zygote64_32.rc:

XML/HTML代码
  1. service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote  
  2.     class main  
  3.     socket zygote stream 660 root system  
  4.     onrestart write /sys/android_power/request_state wake  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart audioserver  
  7.     onrestart restart cameraserver  
  8.     onrestart restart media  
  9.     onrestart restart netd  
  10.     writepid /dev/cpuset/foreground/tasks  
  11.   
  12. service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary  
  13.     class main  
  14.     socket zygote_secondary stream 660 root system  
  15.     onrestart restart zygote  
  16.     writepid /dev/cpuset/foreground/tasks  

  可以看到zygote的class是main, 它是在on nonencrypted时被启动的,如下:

XML/HTML代码
  1. on boot  
  2.     # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.     ... ...  
  7.     class_start core  
  8.   
  9. on nonencrypted  
  10.     # A/B update verifier that marks a successful boot.  
  11.     exec - root cache -- /system/bin/update_verifier nonencrypted  
  12.     class_start main  
  13.     class_start late_start  

  至此,Init.cpp的main函数分析完毕!init进程已经启动完成,一些重要的服务如core服务和main服务也都启动起来,并启动了zygote(/system/bin/app_process64)进程,zygote初始化时会创建虚拟机,启动systemserver等。

]]>
Android开发教程http://www.teaching4real.com/android/course/712.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=712http://www.jizhuomi.com/android/cmd.asp?act=tb&id=712&key=562b7475
Android Studio(二十七):Android Studio内存a@b.com (鸡啄米)http://www.jizhuomi.com/android/environment/711.htmlWed, 06 Sep 2017 09:31:35 +0800http://www.jizhuomi.com/android/environment/711.html  Android Monitor提供了一个Memory Monitor,所以你可以非常容易的监测应用性能和内存使用,可以发现无用的对象,本地内存泄漏和连接设备的内存使用。Memory Monitor显示你的应用如何分配内存,并且用可视化的方式展示出来:

  1、 根据时间显示一个图形的可用和已分配的Java内存

  2、 根据时间显示垃圾处理事件

  3、 初始化垃圾处理事件

  Memory Monitor工作流程

  为了检测和优化你的内存使用,典型的工作流程是运行你的应用,然后执行如下操作:

  1、 使用Memory Monitor检测应用来查看垃圾处理是否对性能造成影响。

  2、 如果你在短时间内看到很多垃圾回收事件,可以进行相关的分析

  3、 开始分配跟踪来确定是否你的代码有问题。

  在Memory Monitor显示一个运行的应用

  通过以下步骤,将运行在特定的设备或虚拟机中查看应用:

  1、 打开一个项目

  2、 在物理设备或虚拟机中运行应用

  3、 显示Android Monitor

  4、 点击Monitors标签并显示Memory Monitor

]]>
Android开发环境http://www.teaching4real.com/android/environment/711.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=711http://www.jizhuomi.com/android/cmd.asp?act=tb&id=711&key=efa097a6
Android开发这些书单你都有了吗?a@b.com (鸡啄米)http://www.jizhuomi.com/android/book/710.htmlMon, 04 Sep 2017 11:13:55 +0800http://www.jizhuomi.com/android/book/710.html 

 

 

 

Android发展如日中天,平均每天有150万部的Android设备被激活,全球有超过二十亿的设备安装Android操作系统。未来Android系统的应用绝不仅局限于手机产业,已迅速扩张到相关领域,如平板电脑、车载系统等。随着Android平台的扩张,引发了Android人才荒,目前移动开发人才需求缺口将达百万,但符合条件的Android工程师还是少数。Android开发工程师成为IT行业炙手可热的岗位之一。今天整理了从Android开发从博一把白菜论坛手机到进阶的十本好书。

 

深入理解Android内核设计思想(第2版)(上下册)

 

  • 国内资深一线开发工程师撰写。


  • 上万名读者实践过的、一本读得懂、用得上,帮助解决工程项目难点的权威指南。


  • 基于AndroidSDK新版本,京东火热预售中。

     

 

Android 源码设计模式解析与实战(第2版)

 

  • 业界第一本Android源码讲解设计模式的书

 

  • 适合Android进阶者,CSDN社区专家精心撰写,业界专家邓凡平、郭霖、任玉刚、徐宜生等鼎力推荐

 

 

 

Android Studio应用开发实战详解

 

  • 几乎涵盖了Android Studio应用开发所能涉及到的所有领域,用具体实例彻底剖析了Android Studio开发的每一个知识点。

 

  • 适合Android初级读者,赠送本书所有的源代码、500个实例代码,1000页PDF电子书,40小时的Android教学视频。

 

 

深入解析Android虚拟机

 

 

  • 适合Android初学者,依次讲解了Java虚拟机基础、Android虚拟机基础,讲解Java虚拟机方面较全的一本参考书。

 

 

  • 遵循“基础讲解—源码分析—核心技术剖析”这一主线。

 

 

Android传感器开发与智能设备案例实战

 

  • 适合Android初学者,全面剖析了与Android传感器和外设应用开发相关的核心技术。


  • 实用性强,通过具体实例进行了实践演练,内容较全面的一本Android传感器和外设应用开发书。

 

 

构建安全的Android App

 

  • 适合Android开发人员,Android安全专家的经验之作。 

 

  • 打造坚固的APP应用程序的实践指南,真实地展示Android App应用中存在的安全问题。

 

Android应用测试指南

 

 

  • 适合测试人员、测试开发人员、测试经理、移动开发人员阅读,一本移动测试实用工具书。


  • 来自Android测试专家的著作,通过缜密的测试打造零Bug 的Android应用。

 

精通Android网络开发

 

 

  • 实例丰富,按照Android 5 新版本编写。


  • 适合Android初学者,内容较全面的一本Android网络应用开发书。

 

Java和Android开发学习指南(第2版)

 

  • 适合想要学习Java语言,想要进行Android应用程序开发的人。


  • 一本书掌握Java开发核心知识和Android平台App开发技能 ,How Tomcat Works作者力作。

 

Android开发进阶:从小工到专家

 

 

  • 适合Android开发初学者,一本专门介绍Android开发的图书 。


  • 不止从博一把白菜论坛手机到精通 更深入讲解开发编程核心知识点,《Android 源码设计模式解析与实战》作者何红辉力作。 

 

Android框架揭秘

 

 

  • 适合有一定开发经验的Android开发人员,韩国年度畅销书,Android内核开发的很好选择。 

 

  • 深入研究分析Android框架内部运行原理与机制,2011年度韩国文化体育观光部优秀学术图书。

 

]]>
Android开发书籍http://www.teaching4real.com/android/book/710.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=710http://www.jizhuomi.com/android/cmd.asp?act=tb&id=710&key=6eed8c8d
Android手机卫士(二十):对话初次设置密码验证过程a@b.com (鸡啄米)http://www.jizhuomi.com/android/example/709.htmlFri, 01 Sep 2017 10:24:57 +0800http://www.jizhuomi.com/android/example/709.htmlAndroid手机卫士(二十):对话初次设置密码验证过程

       首先添加上图按钮的监听事件代码

Java代码
  1. /** 
  2.  * 设置密码对话框 
  3.  */  
  4. private void showSetPsdDialog() {  
  5.     //需要自己去定义对话框的显示样式,所以要调用dialog.setView(view);  
  6.     Builder builder = new Builder(this);  
  7.     final AlertDialog dialog = builder.create();  
  8.     final View view = inflate(this, R.layout.dialog_set_psd, null);  
  9.     //让对话框显示一个自己定义的对话框界面效果  
  10.     dialog.setView(view);  
  11.     dialog.show();  
  12.   
  13.     Button bt_submit = (Button) view.findViewById(R.id.bt_submit);  
  14.     Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel);  
  15.   
  16.     bt_submit.setOnClickListener(new OnClickListener() {  
  17.         @Override  
  18.         public void onClick(View v) {  
  19.             EditText et_set_psd = (EditText) view.findViewById(R.id.et_set_psd);  
  20.             EditText et_confirm_psd = (EditText) view.findViewById(R.id.et_confirm_psd);  
  21.             String psd = et_set_psd.getText().toString();  
  22.             String confirmPsd = et_confirm_psd.getText().toString();  
  23.             if(!TextUtils.isEmpty(psd) && !TextUtils.isEmpty(confirmPsd)){  
  24.                 //进入用户手机防盗模块  
  25.                 if(psd.equals(confirmPsd)) {  
  26.                     Intent intent = new Intent(getApplicationContext(), testActivity.class);  
  27.                     startActivity(intent);  
  28.                     //跳转到新的界面以后需要去隐藏对话框  
  29.                     dialog.dismiss();  
  30.                 } else {  
  31.                     ToastUtil.show(getApplicationContext(),"密码不一致");  
  32.                 }  
  33.   
  34.             }else{  
  35.                 //提示用户密码输入为空的情况  
  36.                 ToastUtil.show(getApplicationContext(),"请输入密码");  
  37.             }  
  38.         }  
  39.     });  
  40.     bt_cancel.setOnClickListener(new OnClickListener() {  
  41.         @Override  
  42.         public void onClick(View view) {  
  43.             dialog.dismiss();  
  44.         }  
  45.     });  
  46. }  

      这里新建一个activity:testActivity用来测试,先看到效果,具体后面再实现

Java代码
  1. public class testActivity extends Activity {  
  2.     @Override  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.   
  6.         TextView textView = new TextView(this);  
  7.         textView.setText("testActivity!");  
  8.   
  9.         setContentView(textView);  
  10.     }  
  11. }  

      然后在清单文件中添加下面的代码:

XML/HTML代码
  1. <activity android:name="com.wuyudong.mobilesafe.activity.testActivity"></activity>
]]>
Android开发实例http://www.teaching4real.com/android/example/709.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=709http://www.jizhuomi.com/android/cmd.asp?act=tb&id=709&key=5d36a465
Android游戏开发实践之NDK与JNI开发03a@b.com (鸡啄米)http://www.jizhuomi.com/android/game/708.htmlWed, 30 Aug 2017 10:45:55 +0800http://www.jizhuomi.com/android/game/708.html本文的目录如下:

1、 环境搭建

要让AndroidStudio支持NDK开发,除了需安装AndroidStudio2.2以上的版本。还得安装NDKCMakeLLDB等工具。
在AndroidStudio中选择Tools->Android->SDK Manager->SDK Tools。如图:

这里简单介绍下:
NDK:是Android开发本地C/C++代码的一套工具集。
CMake:一套跨平台的构建工具,可以Gradle脚本配合使用来构建你的本地库。在AndroidStudio中,Google默认和推荐使用CMake进行编译,当然,你仍然可以继续使用ndk-build来编译你的本地代码。注意:32位的操作系统可能会装不上。
LLDB: 一种调试器,Android Studio中可以使用它来调试本地代码。

2、 创建一个支持C/C++的项目

这里所说的创建一个支持C/C++的项目,可以理解为创建一个NDK项目,但又包含两种方式,分别是从零开始新建一个支持C/C++开发的项目和在原有项目的基础上让它支持C/C++开发。下面对这两种方式分别进行说明下。

2.1 新建项目

如果安装好了,上面介绍的几个工具(主要是NDK),并且AndroidStudio2.2以上的版本,新建项目的时候,会看到这个选项。如图:

创建项目时,勾选C++支持。


项目中所用的C++标准可以选择默认或者支持C++11,以及是否支持异常和rtti特性。


创建完项目,会比一般的Android项目多出cpp目录以及CMakeLists.txt的文件。


这里指定NDK的路径。即,上面环境搭建里安装的ndk,会下载到AndroidStudio根目录下的ndk-bundle文件夹中。


make一下当前新创建的工程,默认会在build/cmake/debug/obj/下生成相应的动态库。

2.2 扩展现有项目

要让现有的Android项目能调用本地C/C++代码或者支持C/C++开发,只需要在原来项目的基础稍加修改即可。步骤如下:

切换到project视图,打开module即上图的app模块,在src/main下右键New->Directory,填写一个文件名,例如:cpp


在刚建的cpp路径下,右键New->C/C++ Source File,输入文件名,若要一并生成相应的.h文件,勾选Create an associated header选项即可。注意,后面可以指定源文件的后缀,例如:.cxx,.hxx


在module的根目录即上图的app根目录,右键New->File,输入CMakeLists.txt。注意:文件名必须为CMakeLists.txt


在module的根目录即上图的app根目录,选择Link C++ Project with Gradle,然后,找到刚创建的CMakeLists.txt文件。将CMakeLists.txt关联到项目中。注意,Build System仍可以选择ndk-build方式进行编译。

当然,这步操作也可以手动完成,相当于在当前module下的build.gradle脚本中,添加了如下代码,

XML/HTML代码
  1. android {  
  2.     //指定使用CMake编译----------------  
  3.     externalNativeBuild {  
  4.         cmake {  
  5.             path 'CMakeLists.txt'  
  6.         }  
  7.     }  
  8.     //--------------------------------  
  9. }   
打开新建的CMakeLists.txt文件,输入如下代码:
XML/HTML代码
  1. cmake_minimum_required (VERSION 3.4.1)  
  2.   
  3. add_library (hellojni SHARED src/main/cpp/hellojni.cpp)  
分别指定CMake要求的版本,add_library中参数分别是,指定生成库的名称,生成库的类型,默认是静态块,即:·a,源码的路径。这里实例只简单介绍下CMake的用法,后续会有专门一篇来介绍下CMake更多高级的用法。

以上完毕,在make一下当前工程,或者rebuild一下,不出意外会在build/intermediates/cmake/debug路径下生成各种libhellojni.so文件。

3、AndroidStudio与Gradle

上面提到,将CMakeLists.txt关联到项目中,会在build.gradle脚本中,添加一段代码即可。可能刚接触AndroidStudio(特别是使用其它IDE开发的,例如:eclipse等)对Gradle不是很了解,这里就抛砖引玉下,简要讲述下gradle脚本的使用。

首先,AndroidStudio是基于IntelliJ IDEA的IDE,在AndroidStudio中新创建的Android工程都形如如下结构:

XML/HTML代码
  1. MyApp    
  2. ├── build.gradle    
  3. ├── settings.gradle    
  4. └── app    
  5.     ├── build.gradle    
  6.     ├── build    
  7.     ├── libs    
  8.     └── src    
  9.         └── main    
  10.             ├── java    
  11.             │   └── com.package.myapp    
  12.             └── res    
  13.                 ├── drawable    
  14.                 ├── layout    
  15.                 └── etc.    
MyApp是项目名,app是模块名,一个项目下可以包含若干个模块。这与eclipse的结构不同,对应到eclipse中,app就相当于项目名,MyApp相当于工作空间。或者类似于VS中解决方案与项目的关系。以上目录结构关系,并不与实际磁盘上的目录结构对应。可以看到,在项目根目录下以及模块目录下,分别有三个.gradle文件。下面,就分别介绍这三个gradle脚本的用途,当然这里主要说的是在AndroidStudio下的gradle的相关使用。

在AndroidStudio中android项目是基于gradle进行构建的(eclipse中可以使用ant来做类似的工作),而gradle是一种基于Groovy语言的DSL(domain-specific language,领域专用语言),而Groovy又是一种基于JVM的动态语言。所以,有java基础的话,理解Groovy会更容易。有关Gradle文档可以看看这个:https://docs.gradle.org/current/dsl/

3.1 project/build.gradle

该build.gradle位于项目的根目录,该文件是定义在这个工程下的所有模块的公共属性。默认如下:

XML/HTML代码
  1. // Top-level build file where you can add configuration options common to all sub-projects/modules.  
  2.   
  3. buildscript {  
  4.     repositories {  
  5.         jcenter()  
  6.     }  
  7.     dependencies {  
  8.         classpath 'com.android.tools.build:gradle:2.2.3'  
  9.   
  10.         // NOTE: Do not place your application dependencies here; they belong  
  11.         // in the individual module build.gradle files  
  12.     }  
  13. }  
  14.   
  15. allprojects {  
  16.     repositories {  
  17.         jcenter()  
  18.     }  
  19. }  
  20.   
  21. task clean(type: Delete) {  
  22.     delete rootProject.buildDir  
  23. }  
以下只是从表象说明下,但实质是Groovy相应的数据结构(闭包,Map等)调用相应方法来动态控制整个构建过程。有关Groovy的讨论可以看看3.3 module/build.gradle
buildscript:定义了全局的相关属性。
repositories:定义了远程仓库的源,即代表你的依赖包的来源。这里将jcenter()作为仓库。
dependencies:定义了android gradle plugin的版本。
allprojects:可以用来定义各个模块的默认属性,你可以不仅仅局限于默认的配置,未来你可以自己创造tasks在allprojects方法体内,这些tasks将会在所有模块中可见。
task clean:执行相关的清理任务。
3.2 project/settings.gradle

该文件位于项目根目录下,也是项目的全局配置文件,该文件的内容如下:

include ':app'

如果,你的项目中有多个模块时,可以依次添加到该文件中。例如:

include ':app',':librarys:Mylibrary'

即在项目根目录下的librarys目录里有个Mylibrary库工程。

3.3 module/build.gradle

该文件位于当前模块的根目录下,通常情况下,该文件只对当前模块起作用。例如:

XML/HTML代码
  1. apply plugin: 'com.android.application'  
  2.   
  3. android {  
  4.     compileSdkVersion 24  
  5.     buildToolsVersion "24.0.2"  
  6.     defaultConfig {  
  7.         applicationId "com.alphagl.test"  
  8.         minSdkVersion 19  
  9.         targetSdkVersion 24  
  10.         versionCode 1  
  11.         versionName "1.0"  
  12.     }  
  13.     buildTypes {  
  14.         release {  
  15.             minifyEnabled false  
  16.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
  17.         }  
  18.     }  
  19.   
  20.     externalNativeBuild {  
  21.         cmake {  
  22.             path 'CMakeLists.txt'  
  23.         }  
  24.     }  
  25. }  
  26.   
  27. dependencies {  
  28.     compile fileTree(dir: 'libs', include: ['*.jar'])  
  29.     compile 'com.android.support:appcompat-v7:24.2.1'  
  30. }  
以上内容,要彻底弄清楚,得深究下Groovy语法。这里,只浅析下。
apply plugin: 'com.android.application':在Groovy中的调用为project.apply([plugin: 'com.android.application']),plugin: 'com.android.application会被作为一个Map参数传递给apply的方法。这里是要将该模块构建为一个应用,若要将模块构建库形式,可以传参为plugin: 'com.android.library

在Groovy中花括号包含的部分称为一个闭包(Closure)。上面的主要两部分androiddependencies,分别对应project中的方法,而参数是相应的闭包结构。通过上面的结构,可以知道该闭包结构还有闭包嵌套和相应的方法。
android:该方法包含了所有的Android属性,而唯一必须包含属性为compileSdkVersion和buildToolsVersion。
compileSdkVersion:该方法包含编译该app时候,使用到的api版本。
buildToolsVersion:该方法包含构建工具的版本号。
defaultConfig:该方法包含app的核心属性,该属性会覆盖在AndroidManifest.xml中的对应属性。
applicationId:该方法定义的属性会覆盖AndroidManifest文件中的包名package name属性。
minSdkVersion:该方法定义的属性表示最小支持api版本。同AndroidManifest中对应的属性。
targetSdkVersion:该方法定义的属性表示目标平台api版本。同AndroidManifest中对应的属性。
buildTypes:该方法定义了构建不同版本的app相关的属性。
release:配置release版本相关的属性。
minifyEnabled:是否进行混淆。
proguardFiles:定义混淆文件的位置。通过getDefaultProguardFile方法获取。
externalNativeBuild:native使用cmake编译。
dependencies:gradle默认的属性之一,定义了所有的依赖包。
compile:编译相应依赖的jar包。组织名,包名,版本号的结构。

以上只简单的列举了下部分属性,对gradle脚本有初步的了解。当然,Groovy在java领域还是有很多应用的。感兴趣的,可以深入了解下。

Groovy文档:
Groovy-Documentation


作者:AlphaGL
出处:http://www.cnblogs.com/alphagl/

]]>
Android游戏开发http://www.jizhuomi.com/android/game/708.html#commenthttp://www.jizhuomi.com/android/http://www.jizhuomi.com/android/feed.asp?cmt=708http://www.jizhuomi.com/android/cmd.asp?act=tb&id=708&key=6a7cad7c