Android开发中Fragment的使用及FragmentManager findFragmentById返回nul

 更新时间:2016年9月20日 19:55  点击:2223
Fragment的使用我们可以分为:使用支持库,创建一个Fragment,创建一个动态UI,多个Fragment之间的通信。文章补充了关于FragmentManager findFragmentById 返回nul如何解决。

Android Fragment的使用


1、使用支持库

如果您的应用需要运行在3.0及以上的版本,可以忽略这部分内容。

如果您的应用使用在3.0以下、1.6及以上的版本,需要使用支持库来构建。

使用支持库的步骤:

1.使用SDK下的SDK Manager工具下载Android Support Package


2. 在您的Android工程的顶级目录下创建一个libs目录

3. 找到您的SDK下的/extras/android/support/v4/android-support-v4.jar,并且拷贝到您的项目的libs下,选中这个jar包 → 右键 → Build Path → Add to Build Path

4.在您的项目的Manifest.xml文件的标签下添加:

android:targetSdkVersion="8"/>

其中targetSdkVersion是您的软件最小支持的版本

5.如果您的项目支持3.0以下的版本,请导入如下的包:android.support.v4.*;

在使用Fragment的Activity请继承FragmentActivity而不是Activity。如果您的系统是3.0或以上版本,同样需要导入类似的包,但是可以使用普通的Activity。

2、创建一个Fragment

Fragment支持在不同的Activity中使用并且可以处理自己的输入事件以及生命周期方法等。可以看做是一个子Activity。

创建一个Fragment

创建一个Fragment和创建一个Activity很类似,继承Fragment类,重写生命周期方法,主要的不同之处就是需要重写一个onCreateView()方法来返回这个Fragment的布局。例子:


Fragment的生命周期方法依赖于Activity的生命周期,例如一个Activity的onPause()的生命周期方法被调用的时候这个Activity中的所有的Fragment的onPause()方法也将被调用。

更多的内容请参照类Fragment。

使用XML添加Fragment到Activity

尽管Fragment可以被多个Activity重用,但是您也必须把Fragment关联到一个FragmentActivity上。可以使用XML布局文件的方式来实现这种关联。

说明:上面的所说的FragmentActivity适用在API在3.0以下的版本,3.0及以上的版本可以使用普通的Activity。

例子:


上面使用fragment标签,android:name=””指定一个添加到xml中的Fragment。对于创建不同的屏幕尺寸布局的更多信息,请阅读支持不同的屏幕尺寸。


当您添加一个片段一个活动布局定义的布局XML文件中的片段,你不能删除在运行时的片段。如果您打算在用户交互和交换片段,你必须添加的活性片段的活动时第一次启动。

3、构建一个灵活的UI

FragmentManager提供了对Activity运行时的Fragment的添加、删除、替换的操作。

在Activity运行期间你可以添加Fragment而不是在XML布局文件中进行定义。如果你打算在Activity中改变Fragment的生命过程。

如果要执行添加、删除、修改的操作,你必须通过FragmentManager的对象获得一个FragmentTransaction对象,通过它的API来执行这些操作。

添加一个Fragment到一个Activity,必须把这个Fragment添加到一个容器视图中。例子:


在Activity中你可以通过getFragmentManager()来获得Fragment对象,然后通过FragmentManager对象的beginFragmentTransaction()方法来获得FragmentTransaction对象。通过它的add()方法来添加一个Fragment到当前的Activity中。

一个FragmentTransaction对象可以执行多个增删修的方法,如果你想把这些修改提交到Activity上,必须在最后调用一下这个对象的commit()方法。例子:



由于不是定义在XML布局中的,所有可以转型删除和修改的操作。

如果替换或者删除一个Fragment然后让用户可以导航到上一个Fragment,你必须在调用commit()方法之前调用addToBackStack()方法添加到回退栈。如果你把这个Fragment添加到了回退栈,在提交之后这个Fragment是会被Stop而不是Destroyed。如果用户导航到这个Fragment,这个Fragment会被Restart而不是重新创建。如果你没有把它添加到回退栈,则在删除或者替换的时候它将被Destroyed。例子:


4、与其他Fragment的交互

两个单独的Fragment之间是不应该进行通信的。应该使用他们所存在的Activity作为沟通的纽带。

为了实现两个Fragment的交互,您可以在Fragment中定义一个接口,然后再这个接口中定义一个方法,在Fragment的onAttach()方法中调用这个接口中的方法。然后让Activity实现这个方法来完成Activity和Fragment之间的通信。例子:

定义接口并调用方法:


实现接口,在这个方法中可以进行与其他Fragment的数据的交互:


可以通过FragmentManager的findFragmentById()来查找一个Fragment。




FragmentManager findFragmentById返回nul解决办法


看Fragment的两种生成方式

一.用xml标签生成

在fragment的宿主activity中添加xml标签

<fragment
        android:id="@+id/fragment_newsContent"
        android:name="com.firstcode.section4_news.NewsContentFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

name为你创建的fragment类

这种方法在activity创建时fragment已经生成了

在Activity中获取fragment实例的操作:

NewsContentFragment fragment = (NewsContentFragment) getFragmentManager().findFragmentById(R.id.fragment_newsContent);

 

二、用java代码动态生成

在fragment的宿主activity的视图文件中添加FrameLayout进行占位

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragmentContainer"/>

在Activity中生成fragment的操作

FragmentManager fm = getFragmentManager();
fm.beginTransaction()
    .add(R.id.fragmentContainer,“你创建的fragment类实例”)
    .commit();

 

问题分析:

    我在使用用FragmentManager.findFragmentById 返回nul的问题就在这,我是通过第二种方式来生成fragment的,也就是说在findFragmentById的实参

    我填的是FrameLayout的Id,而非fragment的Id 所以会返回null

解决方案:

      1.如果是静态生成fragment,获取fragment实例用getFragmentManager().findFragmentById
      2.如果是java代码动态生成fragment,获取fragment实例直接new 一个就好了 没必要用getFragmentManager().findFragmentById
      3.注意xml文件中的标签FrameLayout与fragment

还有个问题,我也是这样解决的 在fragment视图里给textview添加文字

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference

原因是通过java代码生成的fragment add里的Id参数填的是fragment的id 所以fragment的视图没有生成

理解两种fragment生成方式最好的文档莫过于google官方的Android Training下面是中文翻译

使用xml标签添加fragment
使用java代码动态添加fragment

本文我们讲讲Android开发中比较高级的内容,应用程序组成部分,Manifest文件,Manifest文件节点,在Android平台的应用程序开发过程中,Manifest文件举足轻重。每一个应用程序都要有一个Manifest文件,他配置了应用程序在Android系统上的基本信息。

Android应用程序的组成部分和Manifest文件

Android应用程序由松散耦合的组件组成,并使用应用程序Manifest绑定到一起;应用程序Manifest描述了每一组件和它们之间的交互方式,还用于指定应用程序元数据、其硬件和平台要求、外部库以及必需的权限。

一、应用程序的基本结构模块

  · Activity:应用程序的表示层。每个UI都是通过Activity类的一个或多个扩展实现的。Activity使用Fragment和视图来布局和显示信息,以及响应用户动作。

  · Service:应用程序中不可见的工作者。运行时没有UI,可以更新数据源和Activity、触发通知和广播Intent。可以用来执行一个运行时间长的任务,或者不需要和用户交互的任务。

  · Content Provider:可共享的持久数据存储器(内容提供者)。用来管理和持久化应用程序数据,通常会与SQL数据库交互。可以通过配置自己的Content Provider来允许其他应用程序访问,也可以访问其他应用。

  · Intent:消息传递框架。Android中大量使用了Intent、Service或者Broadcast Receiver广播消息,以及请求对特定的一条数据执行操作。

  · Broadcast Receiver: Intent侦听器(广播接收者)。可以监听到那些匹配指定的过滤标准的Intent广播。它会自动地启动应用程序来响应某个接收到Intent。

  · Widget:可视化应用程序组件。它是Broadcast Receiver的特殊变体,可用于创建动态的交互式应用程序组件,用户可以把这些组件添加到他们的主屏幕上。

  · Notification:它允许向用户发送信号,但却不会过分吸引他们的注意力或者打断他们当前的Activity。它们是应用程序不可见或者不活动时吸引用户注意的首选方法。

二、Manifest文件简介

  每一个Android项目都包含一个Manifest文件——Android Manifest.xml,它存储在项目层次中的最底层。Manifest可以定义用用程序及其组件和需求的结构和元数据。

  Manifest包含了组成应用程序的每一个Activity、Service、Content Provider和Broadcast Receiver的节点,并使用Intent Filter和权限来确定这些组件和其他应用程序是如何交互的。此文件还可以指定应用程序的元数据(图标、版本号、主题等等) 以及额外的顶层节点,这些节点可以指定必需的安全权限和单元测试,以及定义硬件、屏幕和平台支持要求。

  Manifest文件有一个根manifest标签构成,该标签带有一个被设为项目包的package属性。它通常包含一个xmls:android属性来提供文件内使用的某些系统属性。

  使用versionCode属性可讲当前的应用版本定义为一个整数,每次版本更新,这个数字都会增加。使用versionName可以定义一个显示给用户的公共版本号。

  installLocation属性,是制定是否允许将程序安装到SD卡上,其值有preferExternal(首选外部存储器)和auto(系统决定)。不指定时,默认按到内部存储器中。由于取出或拒绝外部存储器存在的问题,以下程序不适合安装到外部存储器及其后果:

  · 具有Widget/Live Wallpaper和Live Folder的应用程序: Widget/Live Wallpaper和Live Folder将从主屏幕上移除,而且重启系统后可能不在可用。

  · 提供不中断服务的应用程序:程序和它运行的服务将被停止,并且不会自动重启。

  · 输入法引擎:安装到外部存储器的任何IME都会被禁用。在外部存储器再次可用后,用户必须重新选择IME。

  · 设备管理器:DeviceAdminReceiver及其管理能力将被禁用。


Manifest文件节点详解

首先看一下Manifest文件最基本的结构:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.codingblock.manifesttest"
    android:versionCode="1"
    android:versionName="1.0"
    android:installLocation="preferExternal" >
    <!-- ...nodes... -->
</manifest>



  manifest标签包含了一些节点(node),定义了组成应用程序的应用程序组建、安全设置、测试类和需求。下面是一些manifest子节点标签:

  · uses-sdk:要想正确的运行程序,需要有minSKDVersion(默认值:1)、maxSDKVersion和targetSDKVersion属性。

  · uses-configuration:使用此节点可以指定应用程序支持的每个输入机制的组合。一般不需要包含这个节点,不过对于需要特殊输入控制的游戏说很有用。以下是它的几个属性:

    · reqFiveWayNav:要求设备有上、下、左、右导航,并且能够单击当前的选项时为true。包括跟踪求和D-pad。

    · reqHarKeyboard:要求设备有硬件键盘时为true。

    · reqKeyboardType:指定键盘类型为nokeys、qwerty、twelvekey、undefined。

    · reqNavigation:导航设备(值:nonav、dpad、trackball、wheel或undefined)。

    · reqTouchScreen:以指定必需的触摸屏输入(notouch、stylus、finger或undefined)。

  · uses-feature:Android可以在各种各样硬件平台上运行。可以使用多个uses-feature节点来指定应用程序需要的每个硬件功能,以避免安装到不包含硬件功能的设备上。(如:NFC、蓝牙、摄像头等等)

  · supports-screens:用于指定应用程序针对那些屏幕尺寸惊醒了设计和测试。当应用程序支持某个设备的屏幕是,一般就会使用开发人员提供的布局文件中的缩放属性来布局。在不支持的设备上运行时,系统可能会应用“兼容模式”来显示应用程序。

  · supports-gl-texture:用于声明应用程序能够提供以一种特定的GL纹理压缩格式压缩的纹理资源。如果应用程序能够多种纹理压缩格式,就必须使用多个supports-gl-texture元素。

  · uses-permission:声明应用程序所需权限。

  · permission:应用程序组件也可以创建权限来限制对共享应用程序组件的访问。(可以使用permission标签来创建权限定义)

  · instrumentation:instrumentation类提供了一个测试框架,用来在应用程序运行时测试应用程序组件。

  · application:一个Manifest只能包含一个application节点。用于指定应用程序的各种元数据(标题、图标和主题)。在开发时,建议将debuggable设为true,以启用调试,发布时可以禁用此属性。application节点包含了Activity、Service、Content Provider和Broadcast Receiver等子节点。并通过创建和是用自己的Application类扩展来管理应用程序的状态。

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:debuggable="true" >
        <!-- ...nodes... -->
    </application>



以下是对application子节点的简单介绍:

· activity:应用程序的每一个Activity都需要一个此节点,并使用andorid:name属性来指定Activity类的名称。必须包含核心的启动Activity和其他所有可显示的Activity。启动一个没有定义的Activity就会抛出运行时异常。每一个activity节点都可以使用intent-filter子标签来定义用于启动该Activity的Intent。(指定类名时,可以使用“.”作为简写方式代替应用程序的包名)如下代码:

<activity
    android:name="com.codingblock.manifesttest.MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>



· service和activity标签一样,需要为应用程中使用的每一Service类添加一个此标签。同样它也支持使用intent-filter子标签来进行运行时绑定。

<service android:name=".MyService">
</service>



· provider:此标签用于指定应用程序中的每一Content Provider。(Content Provider用来管理数据库访问和共享)

<provider android:name=".MyContentProvider"
    android:authorities="com.codingblock.manifesttest.MyContentProvider">
</provider>



· receiver:通过添加receiver标签,可以注册一个Broadcast Receiver,而不用事先启动应用程序。一旦注册了之后,无论何时,只要与它相匹配的Intent被系统或应用程序广播出来,它就会立即执行。通过在manifest中注册一个Broadcast Receiver,可以使这个进程实现完全自治。如果一个匹配的Intent被广播了,则应用程序就会自动启动,并且你注册的Broadcast Receiver也会开始执行。每一个receiver节点都允许使用intent-filter子标签来定义可以用来触发接收器的Intent:

<receiver android:name=".MyIntentReceiver">
    <intent-filter>
        <action android:name="com.codingblock.manifesttest.MyIntentReceiver"/>
    </intent-filter>
</receiver>



· uses-library:用于指定该应用程序需要的共享库。

本文章给各位介绍是一篇Android开发之自定义Spinner的例子,因为安卓自带的spinner不适合设计要求了,所以需要自己做一个,下面看例子。


最近在做的项目中有很多下拉框,为了实现方便就用了Android 自带的Spinner,但是自带的Spinner的样式又不符合要求,就学习了一下自定义Spinner。下面是整个步骤:

1.准备好图片

2.style中定义

<!-- spinner -->
<style name="spinner_style">
 <item name="android:background">@drawable/spinner</item>
<item name="android:paddingLeft">5dip</item>
 
3.调用

<Spinner
android:id="@+id/field_item_spinner_content"
style="@style/spinner_style"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>


4.在layout中定义simple_spinner_item.xml

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:gravity="center_vertical"
android:textColor="#808080"
android:singleLine="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

5.java代码

ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext,R.layout.simple_spinner_item);
String level[] = getResources().getStringArray(R.array.affair_level);//资源文件
for (int i = 0; i < level.length; i++) {
    adapter.add(level[i]);
      }
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

效果图:

 

Screenshot_2015-08-20-07-17-28
android应用开发中,加载显示大图片,我们可以使用BitmapFactory.Options,本文我们来讲讲BitmapFactory.Options的使用及BitmapFactory.Options避免 内存溢出 OutOfMemoryError的优化方法。

android中BitmapFactory.Options的使用是在加载图片时,就从图片的加载和使用说起

怎样获取图片的大小?
首先我们把这个图片转成Bitmap,然后再利用Bitmap的getWidth()和getHeight()方法就可以取到图片的宽高了。
新问题又来了,在通过BitmapFactory.decodeFile(String path)方法将突破转成Bitmap时,遇到大一些的图片,我们经常会遇到OOM(Out Of Memory)的问题。怎么避免它呢?
这就用到了我们上面提到的BitmapFactory.Options这个类。
BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds 。SDK中对这个成员的说明是这样的:
If set to true, the decoder will return null (no bitmap), but the out…
也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。
示例代码如下:


BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

Bitmap bmp = BitmapFactory.decodeFile(path, options);/* 这里返回的bmp是null */

BitmapFactory.Options options = new BitmapFactory.Options();


options.inJustDecodeBounds = true;

Bitmap bmp = BitmapFactory.decodeFile(path, options);/* 这里返回的bmp是null */

这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了。
有了宽,高的信息,我们怎样在图片不变形的情况下获取到图片指定大小的缩略图呢?
比如我们需要在图片不变形的前提下得到宽度为200的缩略图。
那么我们需要先计算一下缩放之后,图片的高度是多少 ,代码如下


int height = options.outHeight * 200 / options.outWidth;

options.outWidth = 200;

options.outHeight = height;

options.inJustDecodeBounds = false;

Bitmap bmp = BitmapFactory.decodeFile(path, options);

image.setImageBitmap(bmp);

int height = options.outHeight * 200 / options.outWidth;

options.outWidth = 200;
options.outHeight = height;
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
image.setImageBitmap(bmp);


这样虽然我们可以得到我们期望大小的ImageView
但是在执行BitmapFactory.decodeFile(path, options);时,并没有节约内存。要想节约内存,还需要用到BitmapFactory.Options这个类里的 inSampleSize 这个成员变量。
我们可以根据图片实际的宽高和我们期望的宽高来计算得到这个值。

options.inSampleSize = options.outWidth / 200;

/*图片长宽方向缩小倍数*

options.inSampleSize = options.outWidth / 200;

/*图片长宽方向缩小倍数*/

另外,为了节约内存我们还可以使用下面的几个字段:

options.inDither=false;/*不进行图片抖动处理*/
options.inPreferredConfig=null; /*设置让解码器以最佳方式解码*/
/* 下面两个字段需要组合使用 */
options.inPurgeable = true;
options.inInputShareable = true;



android的BitmapFactory.Options避免内存溢出OOM的优化方法

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,
因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。

因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,
decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,
无需再使用java层的createBitmap,从而节省了java层的空间。
如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而跟有效阻止抛out of Memory异常
另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各种分辨率来自动适应,
使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源,
否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。

另外,以下方式也大有帮助:
1. InputStream is = this.getResources().openRawResource(R.drawable.pic1);
     BitmapFactory.Options options=new BitmapFactory.Options();
     options.inJustDecodeBounds = false;
     options.inSampleSize = 10;   //width,hight设为原来的十分一
     Bitmap btp =BitmapFactory.decodeStream(is,null,options);
2. if(!bmp.isRecycle() ){
         bmp.recycle()   //回收图片所占的内存
         system.gc()  //提醒系统及时回收
}

以下奉上一个方法:

Java代码

/**
 * 以最省内存的方式读取本地资源的图片
 * @param context
 * @param resId
 * @return
 */  
 public static Bitmap readBitMap(Context context, int resId){  
     BitmapFactory.Options opt = new BitmapFactory.Options();  
     opt.inPreferredConfig = Bitmap.Config.RGB_565;   
     opt.inPurgeable = true;  
     opt.inInputShareable = true;  
     //获取资源图片  
     InputStream is = context.getResources().openRawResource(resId);  
     return BitmapFactory.decodeStream(is,null,opt);  
 }


 

优化Dalvik虚拟机的堆内存分配

对 于Android平台来说,其托管层使用的Dalvik JavaVM从目前的表现来看还有很多地方可以优化处理,比如我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉GC处理,使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。当然具体 原理我们可以参考开源工程,这里我们仅说下使用方法:   private final static floatTARGET_HEAP_UTILIZATION = 0.75f; 在程序onCreate时就可以调用 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 即可。

介绍一下图片占用进程的内存算法吧。
android中处理图片的基础类是Bitmap,顾名思义,就是位图。占用内存的算法如下:
图片的width*height*Config。
如果Config设置为ARGB_8888,那么上面的Config就是4。一张480*320的图片占用的内存就是480*320*4 byte。
前面有人说了一下8M的概念,其实是在默认情况下android进程的内存占用量为16M,因为Bitmap他除了java中持有数据外,底层C++的 skia图形库还会持有一个SKBitmap对象,因此一般图片占用内存推荐大小应该不超过8M。这个可以调整,编译源代码时可以设置参数。

android应用开发中,如何卸载动态加载的库?本文我们提供了使用System.loadLibrary卸载及Android Jni来实现。

android 加载库后,如果重复加载同一个库,会出现已经加载得警告,也就是说,就不会重新加载so文件。这时候需要kill掉对应得activity,然后重新启动activity就可以使得so重新加载,对应代码:

int pid = android.os.Process.myPid();

android.os.Process.killProcess(pid);

Android Jni 用动态库的加载与卸载函数说明


一、当 Android 的 Virtual Machine 执行到 System.loadLibrary( "动态库名" ) 函数时,

首先会去执行 C 语言动态库里的 JNI_OnLoad 函数。
它的用途有两个:
1)告诉 Virtual Machine 当前动态库使用了哪个版本的 Jni。
  如果当前动态库中没有提供 JNI_OnLoad 函数,
  Virtual Machine 会默认为动态库使用的是最老的 Jni 1.1 版本。
  由于新版 Jni 做了许多扩充,例如 Jni 1.4 的 java.nio.ByteBuffer。
2)动态库的开发者可以在 JNI_OnLoad 函数中进行动态库内的初始化设置(Initialization),
  将此动态库中提供的各个本地函数(Native Function)登记到 Virtual Machine 里,
  以便能加快以后调用动态库中的本地函数的效率,就是初始化设置的重要一项。
  应用层级的 Java 类通过 Virtual Machine 才能调用到动态库中的本地函数。
  如果没有注册登记过的话,Virtual Machine 就在 动态库名.so 里寻找要调用的本地函数。
  如果需要连续调用很多次且每次都需要寻找一遍的话,会多花许多时间。
  因此 C 语言动态库开发者可以自已将动态库中的本地函数向 Virtual Machine 进行注册登记。
代码示例:(注:由于新浪博客不支持 C 注释,所以请将 /* */ 想像替换为 /星 星/)
jint
JNI_OnLoad( JavaVM* vm,
            void*   reserved )
{
    jint    jintResult = -1;
    JNIEnv* env = NULL;
    /*Reference types, in C.
       typedef void*   jobject;
       typedef jobject jclass; */
    jclass  cls = NULL;

    /* typedef struct {
           const char* name;      /* Java 代码中调用的函数名字 */
           const char* signature; /* 描述了函数的 参数 和 返回值 */
           void*       fnPtr;     /* 函数指针转成无符号指针 */
       } JNINativeMethod;
       其中比较复杂的是第二个参数,
       例如 "()V" 或 "(II)V" 或 "(Ljava/lang/String;)V"
       实际上这些字符是与函数的 参数 及 返回值 类型是一一对应的,
       括号()中的字符表示参数,括号后面的则代表返回值,
       例如 "()V" 就表示 void 函数名();
          "(II)V" 就表示 void 函数名( int, int );
          "(Ljava/lang/String;)V" 就表示 void 函数名( jstring );
       具体的每一个字符所表示的意义下面部分有所详见 */
    /* 动态库中的本地函数信息数组 */
    JNINativeMethod aJNINativeMethod[] = {
        { "MeasureDistance",
          "(Ljava/lang/String;)V",
          (void*)Java_MyJni_MyNDK_MyDemo_MyJniNDKDemo_MeasureDistance }
    };
     /* #if defined(__cplusplus)
           typedef _JNIEnv JNIEnv;
           typedef _JavaVM JavaVM;
       #else
           typedef const struct JNINativeInterface* JNIEnv;
           typedef const struct JNIInvokeInterface* JavaVM;
       #endif */
    /* JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint); */
    /* GetEnv()函数返回的 Jni 环境对每个线程来说是不同的,*/
    /* 因此我们必须在每次进入函数时都要重新获取 */
    if ( JNI_OK != (*vm)->GetEnv( vm,
                                  (void**)env,
                                  JNI_VERSION_1_6 ) )
    {
        /* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
        __android_log_write( ANDROID_LOG_INFO,               /* 日志信息 */
                             "MyJniDemo",                    /* 日志标签 */
                             "Call JavaVM::GetEnv failed" ); /* 日志内容 */
       return jintResult; /* 此时返回的是负壹(-1) */
    }
    /* 如果 将动态库中的本地函数向 Virtual Machine 进行注册登记失败 的话,则 */
    /* 由于 aJNINativeMethod 是一组 函数名称 与 函数指针 的对照表,
       在程序执行期间可以多次调用registerNativeMethods函数来更换注册登记本地函数 */
    /* #if defined(__cplusplus)
           typedef _JNIEnv JNIEnv;
           typedef _JavaVM JavaVM;
       #else
           typedef const struct JNINativeInterface* JNIEnv;
           typedef const struct JNIInvokeInterface* JavaVM;
       #endif */
    /* Reference types, in C.
       typedef void*   jobject;
       typedef jobject jclass; */
    /* struct JNINativeInterface 里的函数指针
       jint (*RegisterNatives)( JNIEnv*,
                                jclass,
                                const JNINativeMethod*,
                                jint ); */
    cls = (*env)->FindClass( env,
                             /* 下面的字符串就是描述 Java 代码中的主类 */
                             "MyJni/MyNDK/MyDemo/MyJniNDKDemo" );
    if ( 0 > (*env)->RegisterNatives( env,
                                      cls,
                                      aJNINativeMethod,
                                      sizeof( aJNINativeMethod ) /
                                      sizeof( aJNINativeMethod[0] ) ) )
    {
        /* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
        __android_log_write( ANDROID_LOG_INFO,                   /*日志信息*/
                             "MyJniDemo",                        /*日志标签*/
                             "Register native methods failed" ); /*日志内容*/
       return jintResult; /* 此时返回的是负壹(-1) */
    }
   
    jintResult = JNI_VERSION_1_6;
    /* 此函数回传 JNI_VERSION_1_6 宏值给 Virtual Machine,
       于是 Virtual Machine 就知道当前动态库所使用的 Jni 版本了 */
    return jintResult; /* JNI_VERSION_1_6(0x00010006) */
}
 
二、JNI_OnUnload 函数与 JNI_OnLoad 函数相对应。

在 JNI_OnLoad 函数中进行的动态库内的初期化设置,
要在 Virtual Machine 释放该动态库时调用 JNI_OnUnload 函数来进行善后清除。
同 Virtual Machine 调用 JNI_OnLoad 一样,
调用 JNI_Unload 函数时,也会将 JavaVM 的指针做为第一个参数传递,原型如下:
jint
JNI_OnUnload( JavaVM* vm,
              void*   reserved );
              
三、JNINativeMethod::signature 描述字符串字符意义说明:

1)基本类型对应关系:

标识符  Jni 类型       C 类型
  V    void           void
  Z    jboolean       boolean
  I    jint           int
  J    jlong          long
  D    jdouble        double
  F    jfloat         float
  B    jbyte          byte
  C    jchar          char
  S    jshort         short
 
2)基本类型数组:(则以 [ 开始,用两个字符表示)

标识串  Jni 类型        C 类型
  [Z   jbooleanArray  boolean[]
  [I   jintArray      int[]
  [J   jlongArray     long[]
  [D   jdoubleArray   double[]
  [F   jfloatArray    float[]
  [B   jbyteArray     byte[]
  [C   jcharArray     char[]
  [S   jshortArray    short[]
 
3)类(class):(则以 L 开头,以 ; 结尾,中间是用 / 隔开的 包 及 类名)

标识串        Java 类型  Jni 类型
L包1/包n/类名;     类名     jobject
例子:
Ljava/net/Socket; Socket      jobject

4)例外(String 类):

标识串               Java 类型  Jni 类型
Ljava/lang/String;  String    jstring

5)嵌套类(类位于另一个类之中,则用$作为类名间的分隔符)

标识串                         Java 类型  Jni 类型
L包1/包n/类名$嵌套类名;              类名      jobject
例子:
Landroid/os/FileUtils$FileStatus;  FileStatus  jobject

[!--infotagslink--]

相关文章

  • 图解PHP使用Zend Guard 6.0加密方法教程

    有时为了网站安全和版权问题,会对自己写的php源码进行加密,在php加密技术上最常用的是zend公司的zend guard 加密软件,现在我们来图文讲解一下。 下面就简单说说如何...2016-11-25
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • ps怎么使用HSL面板

    ps软件是现在很多人都会使用到的,HSL面板在ps软件中又有着非常独特的作用。这次文章就给大家介绍下ps怎么使用HSL面板,还不知道使用方法的下面一起来看看。 &#8195;...2017-07-06
  • 详解redis desktop manager安装及连接方式

    这篇文章主要介绍了redis desktop manager安装及连接方式,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15
  • Plesk控制面板新手使用手册总结

    许多的朋友对于Plesk控制面板应用不是非常的了解特别是英文版的Plesk控制面板,在这里小编整理了一些关于Plesk控制面板常用的使用方案整理,具体如下。 本文基于Linu...2016-10-10
  • 使用insertAfter()方法在现有元素后添加一个新元素

    复制代码 代码如下: //在现有元素后添加一个新元素 function insertAfter(newElement, targetElement){ var parent = targetElement.parentNode; if (parent.lastChild == targetElement){ parent.appendChild(newEl...2014-05-31
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 使用GruntJS构建Web程序之构建篇

    大概有如下步骤 新建项目Bejs 新建文件package.json 新建文件Gruntfile.js 命令行执行grunt任务 一、新建项目Bejs源码放在src下,该目录有两个js文件,selector.js和ajax.js。编译后代码放在dest,这个grunt会...2014-06-07
  • 使用percona-toolkit操作MySQL的实用命令小结

    1.pt-archiver 功能介绍: 将mysql数据库中表的记录归档到另外一个表或者文件 用法介绍: pt-archiver [OPTION...] --source DSN --where WHERE 这个工具只是归档旧的数据,不会对线上数据的OLTP查询造成太大影响,你可以将...2015-11-24
  • 如何使用php脚本给html中引用的js和css路径打上版本号

    在搜索引擎中搜索关键字.htaccess 缓存,你可以搜索到很多关于设置网站文件缓存的教程,通过设置可以将css、js等不太经常更新的文件缓存在浏览器端,这样访客每次访问你的网站的时候,浏览器就可以从浏览器的缓存中获取css、...2015-11-24
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • jQuery 1.9使用$.support替代$.browser的使用方法

    jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support 。 在更新的 2.0 版本中,将不再支持 IE 6/7/8。 以后,如果用户需要支持 IE 6/7/8,只能使用 jQuery 1.9。 如果要全面支持 IE,并混合...2014-05-31
  • 安装和使用percona-toolkit来辅助操作MySQL的基本教程

    一、percona-toolkit简介 percona-toolkit是一组高级命令行工具的集合,用来执行各种通过手工执行非常复杂和麻烦的mysql和系统任务,这些任务包括: 检查master和slave数据的一致性 有效地对记录进行归档 查找重复的索...2015-11-24
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • MySQL日志分析软件mysqlsla的安装和使用教程

    一、下载 mysqlsla [root@localhost tmp]# wget http://hackmysql.com/scripts/mysqlsla-2.03.tar.gz--19:45:45-- http://hackmysql.com/scripts/mysqlsla-2.03.tar.gzResolving hackmysql.com... 64.13.232.157Conn...2015-11-24
  • php语言中使用json的技巧及json的实现代码详解

    目前,JSON已经成为最流行的数据交换格式之一,各大网站的API几乎都支持它。我写过一篇《数据类型和JSON格式》,探讨它的设计思想。今天,我想总结一下PHP语言对它的支持,这是开发互联网应用程序(特别是编写API)必须了解的知识...2015-10-30
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02