How To Use Android CursorLoader – Example

Objective

Main objective of this blog post is to give you an idea about Android CursorLoader

 

Introduction:

This tutorial will teach you how to use cursorloader in android. The concept of Loaders was introduced in Android 3.0 (API Level 11).

There are three key benefits of using a CursorLoader:

  1. The query is handled on a background thread for you (courtesy of being built on AsyncTaskLoader) so that large data queries do not block the UI. This is something the docs recommended for you to do when you’re using a plain Cursor, but now it’s done under the hood.
  2.  CursorLoader is auto-updating. In addition to performing the initial query, CursorLoader also registers a ContentObserver with the dataset you requested and calls forceLoad() on itself when the data set changes.
  3. This results in getting async callbacks anytime the data changes in order to update the view.

Now follow these simple steps: Here we will create a list of all the contacts stored in an android device.

Step 1 Create New Android Project

Create new Android project File >> New >> Android from the given path.

android-application-project

Here we have given Application name as CursorLoaderDemo. Now choose minimum required SDK level to API 11. CursorLoader is introduced in API 11, so it is available for only API 11 and greater than API 11.

new-android-application

Step 2 FragmentActivity

Extend your class to FragmentActivity rather than Activity or ActionBarActivity and Implement LoaderCallBack interface.

  • import android.database.Cursor;
  • import android.net.Uri;
  • import android.os.Bundle;
  • import android.provider.ContactsContract;
  • import android.support.v4.app.FragmentActivity;
  • import android.support.v4.app.LoaderManager.LoaderCallbacks;
  • import android.support.v4.content.CursorLoader;
  • import android.support.v4.content.Loader;
  • import android.widget.ListView;
  • public class MainActivity extends FragmentActivity implements
  • LoaderCallbacks {
  • ListView lstContact;
  • CustomContactAdapter adapter;
  • @Override
  • protected void onCreate(Bundle savedInstanceState) {
  • super.onCreate(savedInstanceState);
  • setContentView(R.layout.activity_main);
  • lstContact = (ListView) findViewById(R.id.lstContacts);
  • getSupportLoaderManager().initLoader(1, null, this);
  • }

Step 3 Implement LoaderCallback interface methods

  • @Override
  • public Loader onCreateLoader(int arg0, Bundle arg1) {
  • return null;
  • }
  • @Override
  • public void onLoadFinished(Loader arg0, Cursor cursor) {
  • }
  • @Override
  • public void onLoaderReset(Loader arg0) {
  • }

More information about LoaderCallbacks interface please refer following link:

Here whenever first time CursorLoader is called, onCreateLoader method is called. To initialize CursorLoader, initLoader (int id, Bundle bundle, Context context) is used. To use this method

  • getSupportLoaderManager().initLoader(1, null, this);

Step 4 onCreateLoader() method

Now, fire query for all the contacts stored on the android device Fire this query in onCreateLoader method of the interface.

  • @Override
  • public Loader onCreateLoader(int arg0, Bundle arg1) {
  • Uri CONTACT_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
  • CursorLoader cursorLoader = new CursorLoader(this, CONTACT_URI, null,
  • null, null, null);
  • return cursorLoader;
  • }

Step 5 Set adapter to listview.

  • @Override
  • public void onLoadFinished(Loader arg0, Cursor cursor) {
  • cursor.moveToFirst();
  • adapter = new CustomContactAdapter(this, cursor);
  • lstContact.setAdapter(adapter);
  • }

Here, in onLoadFinished, a cursor is passed which contains all the data that we are passing to CustomContentAdapter.

  • @Override
  • public void onLoaderReset(Loader arg0) {
  • }

onLoaderReset() is called when there is a change in the data source.

Step 6 MainActivity.java file

  • package com.tag.cursorloaderdemo;
  • import android.database.Cursor;
  • import android.net.Uri;
  • import android.os.Bundle;
  • import android.provider.ContactsContract;
  • import android.support.v4.app.FragmentActivity;
  • import android.support.v4.app.LoaderManager.LoaderCallbacks;
  • import android.support.v4.content.CursorLoader;
  • import android.support.v4.content.Loader;
  • import android.widget.ListView;
  • public class MainActivity extends FragmentActivity implements
  • LoaderCallbacks {
  • ListView lstContact;
  • CustomContactAdapter adapter;
  • @Override
  • protected void onCreate(Bundle savedInstanceState) {
  • super.onCreate(savedInstanceState);
  • setContentView(R.layout.activity_main);
  • lstContact = (ListView) findViewById(R.id.lstContacts);
  • getSupportLoaderManager().initLoader(1, null, this);    
  • }
  • @Override
  • public Loader onCreateLoader(int arg0, Bundle arg1) {
  • Uri CONTACT_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
  • CursorLoader cursorLoader = new CursorLoader(this, CONTACT_URI, null,
  • null, null, null);
  • return cursorLoader;
  • }
  • @Override
  • public void onLoadFinished(Loader arg0, Cursor cursor) {
  • cursor.moveToFirst();
  • adapter = new CustomContactAdapter(this, cursor);
  • lstContact.setAdapter(adapter);
  • }
  • @Override
  • public void onLoaderReset(Loader arg0) {
  • }
  • }
 

Step 7 CustomContentAdapter.java file

  • package com.tag.cursorloaderdemo;
  • import android.content.Context;
  • import android.database.Cursor;
  • import android.graphics.Bitmap;
  • import android.graphics.Matrix;
  • import android.graphics.drawable.BitmapDrawable;
  • import android.graphics.drawable.Drawable;
  • import android.net.Uri;
  • import android.provider.ContactsContract.CommonDataKinds.Phone;
  • import android.provider.MediaStore;
  • import android.view.LayoutInflater;
  • import android.view.View;
  • import android.view.ViewGroup;
  • import android.widget.BaseAdapter;
  • import android.widget.ImageView;
  • import android.widget.LinearLayout;
  • import android.widget.TextView;
  • public class CustomContactAdapter extends BaseAdapter {
  • Cursor cursor;
  • Context mContext;
  • LayoutInflater inflater;
  • public CustomContactAdapter(Context context, Cursor cursor) {
  • mContext = context;
  • this.cursor = cursor;
  • inflater = (LayoutInflater) mContext
  • .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  • }
  • @Override
  • public int getCount() {
  • return cursor.getCount();
  • }
  • @Override
  • public Object getItem(int position) {
  • return null;
  • }
  • @Override
  • public long getItemId(int position) {
  • return position;
  • }
  • @Override
  • public View getView(int position, View convertView, ViewGroup parent) {
  • View view = convertView;
  • Holder holder;
  • cursor.moveToPosition(position);
  • if (view == null) {
  • view = inflater.inflate(R.layout.contact_list_element, parent,
  • false);
  • holder = new Holder();
  • holder.tvCOntactName = (TextView) view
  • .findViewById(R.id.tvContactName);
  • holder.tvContactNumber = (TextView) view
  • .findViewById(R.id.tvContactNumber);
  • holder.ivContactImage = (ImageView) view.findViewById(R.id.ivContactImage);
  • view.setTag(holder);
  • } else {
  • holder = (Holder) view.getTag();
  • }
  • holder.tvCOntactName.setText(cursor.getString(cursor
  • .getColumnIndex(Phone.DISPLAY_NAME)));
  • holder.tvContactNumber.setText(cursor.getString(cursor
  • .getColumnIndex(Phone.NUMBER)));
  • String imageUri = cursor.getString(cursor
  • .getColumnIndex(Phone.PHOTO_URI));
  • try {
  • Bitmap bitmap = MediaStore.Images.Media.getBitmap(
  • mContext.getContentResolver(), Uri.parse(imageUri));
  • holder.ivContactImage.setImageBitmap(bitmap);
  • scaleImage(holder.ivContactImage);
  • } catch (Exception e) {
  • holder.ivContactImage.setImageResource(R.drawable.contact);
  • scaleImage(holder.ivContactImage);
  • }
  • return view;
  • }
  • class Holder {
  • TextView tvCOntactName, tvContactNumber;
  • ImageView ivContactImage;
  • }
  • private void scaleImage(ImageView imageView) {
  • Drawable drawing = imageView.getDrawable();
  • if (drawing == null) {
  • }
  • Bitmap bitmap = ((BitmapDrawable) drawing).getBitmap();
  • int width = bitmap.getWidth();
  • int height = bitmap.getHeight();
  • int bounding = dpToPx(50);
  • float xScale = ((float) bounding) / width;
  • float yScale = ((float) bounding) / height;
  • float scale = (xScale <= yScale) ? xScale : yScale;
  • Matrix matrix = new Matrix();
  • matrix.postScale(scale, scale);
  • Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height,
  • matrix, true);
  • width = scaledBitmap.getWidth(); // re-use
  • height = scaledBitmap.getHeight(); // re-use
  • BitmapDrawable result = new BitmapDrawable(scaledBitmap);
  • imageView.setImageDrawable(result);
  • LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) imageView
  • .getLayoutParams();
  • params.width = width;
  • params.height = height;
  • imageView.setLayoutParams(params);
  • }
  • private int dpToPx(int dp) {
  • float density = mContext.getResources().getDisplayMetrics().density;
  • return Math.round((float) dp * density);
  • }
  • }
 

Step 8 activity_main.xml file

  • <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  • xmlns:tools="http://schemas.android.com/tools"
  • android:layout_width="match_parent"
  • android:layout_height="match_parent"
  • android:gravity="center"
  • tools:context="${relativePackage}.${activityClass}" >
  • <ListView
  • android:id="@+id/lstContacts"
  • android:layout_width="match_parent"
  • android:layout_height="wrap_content"
  • android:layout_centerHorizontal="true"
  • android:layout_centerVertical="true" >
  • </ListView>
  • </RelativeLayout>
 

Step 9 Layout file for CustomContentAdapter’s element

  • <?xml version="1.0" encoding="utf-8"?>
  • <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  • android:layout_width="match_parent"
  • android:layout_height="match_parent"
  • android:gravity="center_vertical"
  • android:orientation="vertical" >
  • <LinearLayout
  • android:layout_width="match_parent"
  • android:layout_height="wrap_content"
  • android:layout_gravity="center_vertical"
  • android:orientation="horizontal" >
  • <LinearLayout
  • android:layout_width="wrap_content"
  • android:layout_height="match_parent" >
  • <ImageView
  • android:id="@+id/ivContactImage"
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:scaleType="fitCenter"
  • android:src="@drawable/ic_launcher" />
  • </LinearLayout>
  • <LinearLayout
  • android:layout_width="0dp"
  • android:layout_height="match_parent"
  • android:layout_gravity="center"
  • android:layout_weight="0.4"
  • android:orientation="vertical" >
  • <TextView
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:layout_gravity="center"
  • android:text="Name"
  • android:textAppearance="?android:attr/textAppearanceSmall" />
  • <TextView
  • android:id="@+id/tvContactName"
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:layout_gravity="center"
  • android:text="Medium Text"
  • android:textAppearance="?android:attr/textAppearanceMedium" />
  • </LinearLayout>
  • <LinearLayout
  • android:layout_width="0dp"
  • android:layout_height="match_parent"
  • android:layout_gravity="center"
  • android:layout_weight="0.3"
  • android:orientation="vertical" >
  • <TextView
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:layout_gravity="center"
  • android:text="Number"
  • android:textAppearance="?android:attr/textAppearanceSmall" />
  • <TextView
  • android:id="@+id/tvContactNumber"
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:layout_gravity="center"
  • android:text="Medium Text"
  • android:textAppearance="?android:attr/textAppearanceMedium" />
  • </LinearLayout>
  • </LinearLayout>
  • </LinearLayout>
 

Step 10 Get Read Contacts permission

  • <uses-permission android:name="android.permission.READ_CONTACTS" />
 

Step 11 AndroidManifest.xml

  • <?xml version="1.0" encoding="utf-8"?>
  • <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  • package="com.tag.cursorloaderdemo"
  • android:versionCode="1"
  • android:versionName="1.0" >
  • <uses-sdk
  • android:minSdkVersion="11"
  • android:targetSdkVersion="21" />
  • <uses-permission android:name="android.permission.READ_CONTACTS" />
  • <application
  • android:allowBackup="true"
  • android:icon="@drawable/ic_launcher"
  • android:label="@string/app_name"
  • android:theme="@style/AppTheme" >
  • <activity
  • android:name=".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>
  • </application>
  • </manifest>

cursor-loader-demo

I hope you find this blog very helpful while working with Android CursorLoader. Let me know in comment if you have any question regarding Android CursorLoader. I will reply you ASAP.

Got an Idea of Android App Development? What are you still waiting for? Contact us now and see the Idea live soon. Our company has been named as one of the best Android App Development Company in India.

– See more at: http://www.theappguruz.com/blog/use-android-cursorloader-example#sthash.IMoXdAbH.dpuf

Advertisements

Useful for Android the development engineer from Github

Many plowing on open space Github, I found assemblage of interesting designs, source the codes, and libraries. And now the time came to share them. Meet assemblage vkusnostej under katom!

1. FreeFlow

The design which allows to create that that napodobii mozajki or tiles from Windows Phone, with assemblage of interesting and beautiful animations, FreeFlow the design is inspired iOS frejmvorkom UICollectionViews .

image

the Hidden text

2. PhotoView

The library for operation with ImageView and if to be more exact to add support Zoom Gestures, is very powerful and floppy, convenient in usage

It is a little about usage

  @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView (R.layout.fragment_edit_it);

        ImageView photoView = (ImageView) findViewById (R.id.image);
        
        PhotoViewAttacher attacher = new PhotoViewAttacher (photoView);
        attacher.setZoomable (true);
    }

image

3. NotBoringActionBar

The design, which shows how to make not boring action bar as in Google application Newsstand

image

4. Picasso

The most powerful library for loading of pictures, conversion, you can easily load photos in yours View from external references, media links, ways to files, resources androida or other application packages.

Many widespread traps of loading of images on Android are processed automatically by means of Picasso:

  • Control of a recycling and cancellings of loading of images in adaptorah
  • Minimum expenses on tranformatsiju images
  • Automatic caching

To tell the truth it is my favourite library 🙂

 //to Steam of lines with instances;
Picasso.with (context)
  .load (url)
  .resize (50, 50)
  .centerCrop ()
  .placeholder (R.drawable.user_placeholder)
  .error (R.drawable.user_placeholder_error)
  .into (imageView)

image

编译一个native c的android应用

原文: Compile a native C Android application
翻译: Zhiwei.Li

通过上网搜索,你可以发现很多种编译Android native应用的方法.我想说的是,不同的控制台应用, 守护程序(daemon), C/C++库,等等.这些程序在你自己的计算机上
编译没有任何问题.

为了给Android编译程序,你需要ARM工具链(toolchain). 我发现有两种主张,分别是使用Android Prebuild toolchain和CodeSourcery
1)Android Prebuild toolchain
Android没有使用传统的libc库.相反,它用了Bionic库,一个由Google开发的,用在Android移动软件平台上的轻量级的libc
Bionic被裁剪到只支持 Android系统.  请看 六百万美元的c程序库

2)CodeSourcery
CodeSourcery是ARM的合作伙伴. 专门为ARM处理器开发增强GUN工具链的,并提供验证过的GNU工具链.这些工具链有很多不同的版本.
对于Android平台,需要 arm-none-linux-gnueabi, 而 arm-none-eabi是没有glibc包含在里面的,主要面向那些编译完整的native库和应用(比如FreeRTOS)
译者注: arm-none-eabi就是用来编译裸机程序的,请参考 最简单的ARM裸机程序

我个人的观点,如果白手起家开始创建一个Android的应用程序,你应该选择Bionic.
但是如果你选择从你的PC环境移植一个库到Android,你应该选择CodeSourcery
如果你使用到线程或者C++异常,Bionic库也不能完全支持它们(实际上,它根本就不支持异常)

agcc.pl是Andrew Ross开发的一个脚本,让你以一种很简单的方法来自动包含常用的库,使用Android的ARM工具链gcc
某种程度上,他像makefile

第1种方法  使用Makefile和Android NDK

AR = arm-linux-androideabi-ar
AS = arm-linux-androideabi-as
CC = arm-linux-androideabi-gcc
CXX = arm-linux-androideabi-g++
LD = arm-linux-androideabi-ld.gold

NDK_KIT = /home/tim/android-ndk-r10b
PLATF_KIT = platforms/android-9

ARM_INC = $(NDK_KIT)/$(PLATF_KIT)/arch-arm/usr/include
ARM_LIB = $(NDK_KIT)/$(PLATF_KIT)/arch-arm/usr/lib

OBJS = hello.o
EXES = hello

hello :  hello.o
	$(LD) --dynamic-linker=/system/bin/linker -nostdlib \
		-rpath-link=$(ARM_LIB) \
		$(ARM_LIB)/crtbegin_dynamic.o \
		-L$(ARM_LIB)  -lc \
		-o hello hello.o

hello.o: hello.c
	$(CC) -I $(ARM_INC) -c hello.c

clean:
	rm -f $(OBJS) $(EXES)

源代码

#include <stdio.h>

int main(int argc, char* argv[])
{
  printf("Hello Android\n");
  return 0;
}

设置环境变量 envsetup.sh

export PATH=$PATH:/home/tim/android-ndk-r10b/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin

最后,运行 make 就可以了

关于 -rpath-link选项,请参考 gcc链接选项

第二种简单方法,使用shell脚本

#!/bin/sh

OS='linux'
ANDROIDSDK='android-14'

PROGDIR='/home/tim/android-ndk-r10b/'

PROGDIR=`cd $PROGDIR && pwd`
ARMEABIGCC=$PROGDIR/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc
ARMEABILIB=$PROGDIR/platforms/$ANDROIDSDK/arch-arm/usr/lib
ARMEABIINC=$PROGDIR/platforms/$ANDROIDSDK/arch-arm/usr/include
ARMEABICRT="$ARMEABILIB/crtbegin_dynamic.o $ARMEABILIB/crtend_android.o"

LINKER=/system/bin/linker

echo "GCC:"$ARMEABIGCC "LIB:"$ARMEABILIB "LINKER":$LINKER "PARAMS:"$@
echo "CRT:"$ARMEABICRT

$ARMEABIGCC $@ -Wl,-rpath-link=$ARMEABILIB,-dynamic-linker=$LINKER -L$ARMEABILIB $ARMEABICRT -I$ARMEABIINC -nostdlib -lc

保存为b
./b hello.c -o hello
就可以了

实际就是运行命令

/home/tim/android-ndk-r10b/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc \
  -Wl,-rpath-link=/home/tim/android-ndk-r10b/platforms/android-16/arch-arm/usr/lib,-dynamic-linker=/system/bin/linker \
  -L/home/tim/android-ndk-r10b/platforms/android-16/arch-arm/usr/lib  \
  /home/tim/android-ndk-r10b/platforms/android-16/arch-arm/usr/lib/crtbegin_dynamic.o   /home/tim/android-ndk-r10b/platforms/android-16/arch-arm/usr/lib/crtend_android.o  \
   -I/home/tim/android-ndk-r10b/platforms/android-16/arch-arm/usr/include -nostdlib -lc \
   hello.c -o hello

crtbegin_dynamic.o 和 crtend_android.o必须配对使用

第三种方法,用–sysroot也是可以的

#!/bin/sh

NDK=/home/tim/android-ndk-r8e
SYSROOT=$NDK/platforms/android-9/arch-arm
CC="$NDK/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc --sysroot=$SYSROOT"
CFLAGS='-march=armv7-a -mfloat-abi=softfp -mfpu=neon'
LDFLAGS='-Wl,--fix-cortex-a8'
$CC $@

http://www.srombauts.fr/2011/03/06/standalone-toolchain/

#include  <stdio.h>
#include <android/log.h>

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "hello-ndk", __VA_ARGS__))

int main(void)
{
    printf("Hello from NDKn");
    LOGI("Hello from NDK");
    return 0;
}

果然厉害,上面这段代码都可以这样编译

./b9 -l log nl.c -o hn

用Makefile也能搞

CC  = arm-linux-androideabi-gcc
CFLAGS  = -Wall -g
LDFLAGS = -llog
SRC =hello-ndk.c
OBJ =$(SRC:.c=.o)
EXE =hello-ndk

all: $(SRC) $(EXE)

$(EXE): $(OBJ)
    $(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
    $(CC) -o $@ -c $< $(CFLAGS)

clean:
    rm -f *.o $(EXE)

编译so库也是可以,厉害

CC  = arm-linux-androideabi-gcc
CFLAGS  = -Wall -g
LDFLAGS = -llog -shared
SRC =hello-ndk.c
OBJ =$(SRC:.c=.o)
EXE =libhello-ndk.so

还可以直接运行 gcc

/home/tim/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc --sysroot=/home/tim/android-ndk-r10/platforms/android-3/arch-arm   -lc -lm   -g main.c -o mm

第4种方法,用ndk-build
创建工程目录hello,然后在其下创建子目录jni
然后在jni下创建两个文件,一个是hello.c,另外一个是Android.mk,内容如下

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=hello.c
LOCAL_MODULE := helloworld
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)

其中 LOCAL_MODULE_TAGS := optional  这行可以不要

进入到hello目录下,运行下面的命令

# export NDK_PROJECT_PATH=`pwd`
# ndk-build
Compile thumb  : helloworld <= hello.c Executable     : helloworld Install        : helloworld => libs/armeabi/helloworld

Android UI主线程如何同非UI线程进行通信

原文 Android Non-UI to UI Thread Communications
作者 Jim White+
翻译 http://zhiwei.li/text/

0. Android的UI线程 和 ANR
在Android平台上, 默认情况下,应用都在一个线程里运行. 这个线程就叫做UI线程. 它频繁地被调用,因为这个县城显示用户界面, 监听用户同应用交互是发生的事件.
如果运行在这个线程里的代码 过分使用这个线程,阻止用户交互(超过5秒钟), 它将引起Android抛出一个 臭名昭著的ANR(Android Not Responsive)错误.

1.
如何对付ANR?你的应用必须创建另外的线程了,将需要长时间运行的工作放到 非UI线程. 如何实现创建另外的线程 有一些选择.
你可以创建并开启你自己的java.lang.Thread
你也可以创建并开启一个AsyncTask(Android自己的线程简化实现机制)

非UI线程处理 长时间运行的工作,比如文件下载.
UI线程还是专注于显示UI和处理用户事件.

生活又重新归于美好了.

然而,不幸地是, 用户界面(UI)不能被非UI线程刷新. 举例来说, 在成功下载玩文件后, 非UI县城不能显示一个AlertDiaglog, 更新TextView窗体, 或者其他任何UI上的变化,来提示文件已经被成功下载了.如果你尝试从非UI线程更新UI. 这个应用可以编译,但是运行时,会得到一个CallFromWrongThreadException的异常.

2.
有几种方法,可以让非UI线程发送请求到UI线程来更新UI.
1)使用runOnUiThread方法
2)使用post()方法
3)使用Handler框架
4)使用广播和广播接受者(还可以带上LocalBroadcastManager)
5)使用AsyncTask的onProgressUpdate()方法

3. 实例设计
为了上面的5种方法, 设计了一个小应用. 它有一个activity(一个屏幕)
含有两个按钮 和一个TextView
一个按钮 用来开启 单独的非UI线程
另外一个 用来 停止 这个线程

运行后, 非UI线程 生成一个随机数, 然后睡眠几秒钟. 这可以模拟长时间运行的工作.
但是, 非UI线程 想将自己每次生成的随机数 显示到TextView中.

sdfds
package li.zhiwei.nonui;

import android.app.Activity;
import android.os.Bundle;

import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends Activity {
    private Button startButton, stopButton;
    private TextView resultsTextView;

    DoSomethingThread randomWork;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startButton = (Button) findViewById(R.id.startButton);
        stopButton = (Button) findViewById(R.id.stopButton);
        resultsTextView = (TextView) findViewById(R.id.resultsTextView);


        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v == startButton) {
                    startGenerating();
                } else {
                    stopGenerating();
                }
            }
         };

         startButton.setOnClickListener(listener);
         stopButton.setOnClickListener(listener);
    }



    private void startGenerating() {
        randomWork = new DoSomethingThread();
        randomWork.start();
    }


    private void stopGenerating() {
        randomWork.interrupt();
        updateResults(getString(R.string.service_off));
    }


    public void updateResults(String results) {
        resultsTextView.setText(results);
    }


    public class DoSomethingThread extends Thread {
       private static final int DELAY = 5000; // 5 seconds
       private static final int RANDOM_MULTIPLIER = 10;

       @Override
       public void run() {
            while (true) {
               int randNum = (int) (Math.random() * RANDOM_MULTIPLIER);
               publishProgress(randNum);
            try {
               Thread.sleep(DELAY);
            } catch (InterruptedException e) {
               return;
            }
        }
    }

    private void publishProgress(int randNum) {
        final String text = String.format(getString(R.string.service_msg), randNum);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                updateResults(text);
            }
        });
    }
  }


}

上面演示的是 runOnUiThread 方法, 它定义在Activity类中
实现方法是,通过调用runOnUiThread() 方法,发送消息 requested action 到UI线程的 event queue (事件队列),
UI线程从队列取出action消息,然后执行

使用runOnUiThread的优缺点:
1. runOnUiThread方法定义在Activity类中. 这意味着 非UI线程 必须 有一些知识或者手段
来获取Activity
在我们上面的例子中,这是很容易做到的.因为我们的非UI线程 就作为一个inner class定义在Activity类中.

2. 如果在UI线程调用 runOnUiThread, 那么会直接调用对应的方法, 而不是 发送消息到自己的队列.

直接用post向view组件发消息

            /*
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    updateResults(text);
                }
            });
            */
            resultsTextView.post(new Runnable() {
                @Override
                public void run() {
                    resultsTextView.setText(txt);
                }
            });

因为post()使用了和runOnUiThread()一样的消息队列.所以 优缺点差不多.
尤其是你的非UI线程 必须知道View组件. 这将允许非UI县城避免直接同Activity代码连接.
也就说说你的 非UI线程 之需要知道一个View, 而不必知道Activity或者其他东西.

另外,即使你在UI线程中调用post(),它也会将消息放到队列, 而不是直接运行相应的方法.这跟runOnUiThread()是不同的.

我们知道, 在UI线程中,有一个消息队列. 放到队列的消息被依次处理. 用户事件(比如一个按钮被按下), 引起事件消息被放到这个队列里.
runOnUiThread()和post()方法被调用, 也会放消息到这个队列里.
你可以更直接地使用消息队列. 比如使用Handler框架.
这个框架让你 构建一个 消息处理器 在UI线程里监听 消息.

首先给MainActivity类添加一个inner class
    private static class HandlerExtension extends Handler {

        private final WeakReference currentActivity;

        public HandlerExtension(MainActivity activity){
            currentActivity = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message message){
            MainActivity activity = currentActivity.get();
            if (activity!= null){
                activity.updateResults(message.getData().getString("result"));
            }
        }
    }

在onCreate()里创建它
    resultHandler = new HandlerExtension(this);

在publishProgress()里发消息给Handler

            Bundle msgBundle = new Bundle();
            msgBundle.putString("result", txt);
            Message msg = new Message();
            msg.setData(msgBundle);
            resultHandler.sendMessage(msg);

这样大费周折的好处是, 非UI类只需要知道 Handler, 不需要知道UI线程任何别的东西
你自己的Handler必须是静态的,并且要对Activity进行弱引用(WeakReference). 这样避免内存泄漏.

使用本地广播

在MainActivity类里创建inner class
    private BroadcastReceiver createBroadcastReceiver() {
        return new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                updateResults(intent.getStringExtra("result"));
            }
        };
    }
在OnCreate()里创建一个广播接受者, 并向 本地广播管理者 注册它
        resultReceiver = createBroadcastReceiver();
        LocalBroadcastManager.getInstance(this).registerReceiver(
                resultReceiver,
                new IntentFilter("li.zhiwei.random.generation"));

为MainActivity添加一个onDestroy()方法
    @Override
    protected void onDestroy() {
        if (resultReceiver != null) {
            LocalBroadcastManager.getInstance(this).unregisterReceiver(
                    resultReceiver);
        }
        super.onDestroy();
    }
在 publishProgress()里发送广播
            Intent intent = new Intent("li.zhiwei.random.generation");
            intent.putExtra("result", txt);
            LocalBroadcastManager.getInstance(MainActivity.this)
                    .sendBroadcast(intent);

发送广播不需要消息队列. 但是它 需要 意图(Intent) 和 Inter listner(这里是broadcast reciever)

使用广播还可以在两个活动之间传递大量的数据

AsyncTask是对Thread+Handler的良好封装

    public class DoSomethingTask extends AsyncTask<void, string,="" void=""> {
        private static final int DELAY = 5000; // 5 seconds
        private static final int RANDOM_MULTIPLIER = 10;

        @Override
        protected void onProgressUpdate(String... values) {
            updateResults(values[0].toString());
            super.onProgressUpdate(values);
        }


        @Override
        protected Void doInBackground(Void... params) {
            String txt="";
            while (true) {
                if (isCancelled()) {
                    break;
                }
                int randNum = (int) (Math.random() * RANDOM_MULTIPLIER);
                txt = String.format(getString(R.string.service_msg), randNum);
                publishProgress(txt);

                try {
                    Thread.sleep(DELAY);
                } catch (InterruptedException e) {
                }
            }
            return null;
        }

    }
开始和停止要改动
    private void startGenerating() {
        randomWork = new DoSomethingTask();
        randomWork.execute();
    }

    private void stopGenerating() {
        randomWork.cancel(true);
        updateResults(getString(R.string.service_off));
    }

据说限制是, AsyncTask只能被执行一次,多次调用会产生异常

Android NDK引用预编译的动态链接库

NDK里有个例子: android-ndk-r10/samples/module-exports/jni
一看就懂了

———————————————————————————–

从r5版本开始,就支持预编译的库(共享和静态). 也就是说在你的应用中,可包含和使用 预先编译的库。

这个功能的用处
1. 你想分发你自己的库给第3方 NDK开发者,但不想把源码给他们
2. 你想使用自己的预编译的库 来加速项目的Build过程。

声明一个预编译的库模块
每个预编译的库,都必须声明为一个独立的模块 给ndk build系统。

目录结构

mylib
   --Android.mk
   --libfoo.so

Android.mk的内容

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)

注意事项
1. 每个预编译的模块都必须有一个名字, 比如上面取的名字是 foo-prebuilt
模块 名字 可以跟 预编译的库的名字 不一样(比如库的名字是 libfoo, 模块的名字是 foo-prebuild)
2. 设置 LOCAL_SRC_FILES 为你提供的预编译库的 路径。注意,这个路径 是相对于 LOCAL_PATH的。 据说也在 LOCAL_PATH/lib 目录里找源so.

另外: 如果你提供的库有多个ABI的版本,还要有点技巧,后面会提到。

3. 包含 PREBUILT_SHARED_LIBRARY (提供共享库) ; 或者 包含 PREBUILT_STATIC_LIBRARY(提供静态库)

在其他模块中 引用上面准备好的预编译模块
只需添加LOCAL_SHARED_LIBRARIES(或者 LOCAL_STATIC_LIBRARIES)声明 到你的 Android.mk中就行了

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := foo-user
LOCAL_SRC_FILES := foo-user.c
LOCAL_SHARED_LIBRARIES := foo-prebuilt
#LOCAL_LDLIBS := -lm -llog

include $(BUILD_EXECUTABLE)

唯一要注意的是,引用 模块名(foo-prebuilt), 而不是库名(libfoo)

为预编译的库 输出头文件
在实际应用中, foo-user.c会依赖于 同库文件一起分发的头文件(foo.h)中声明的函数或变量
也就是说,在 foo-user.c 将会有如下代码

#include <foo.h>

构建你的foo-user模块时,必须提供 预编译模块的 头文件 以及 头文件的包含路径 给编译器。

假设头文件爱你 放在 预编译模块目录下的 include 目录, 我们可以在预编译模块的Android.mk中使用export

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include

include $(PREBUILT_SHARED_LIBRARY)

LOCAL_EXPORT_C_INCLUDES 可以让 别的引用模块 找到 适合的头文件
别的引用模块,将在 自己的 Android.mk里用 LOCAL_C_INCLUDE 来找(好像不用声明这个, LOCAL_LDFLAGS也不用)

多种ABI

armeabi   目标CPU是ARM v5 TE或者之后的架构
armeabi-v7a 目标CPU是 ARM v7或者之后的架构
x86      
mips

需要修改 预编译模块里定义的 LOCAL_SRC_FILES

 LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libfoo.so

Android Studio如何同NDK集成

原文: http://ph0b.com/android-studio-gradle-and-ndk-integration/

将native库(.so文件)集成到你的APK包

以前的方法是把.so放到.jar包里, 现在很简单了

在 src/main/ 目录下(该目录下有 java和res两个子目录), 创建一个 jniLibs 目录
再创建相应ABI子目录(如 x86, mips, armeabi-v7a, armeabi)
最后,把相应的so文件放入对应的目录即可
build时,所有的.so文件都会被集成到你的apk里


如果jniLibs这个目录不适合你(你可能生成你的.so文件放在其他地方), 你可以在build.gradle里指定位置

android {
    ...
    sourceSets.main {
        jniLibs.srcDir 'src/main/libs'
    }
}

是在 模块目录下的 build.gradle里, 工程目录下的build.gradle里没有android {] 这个条目


有洁癖的人,喜欢为每个架构单独生成一个APK,这样, APK的体积会比较小

那就可以使用abiFilter这个属性, 这个属性默认是设置为 all, 将所有平台的.so都打包进apk

也是在build.gradle的android条目下添加一些东西

android{
  ...
  productFlavors {
        x86 {
            ndk {
                abiFilter "x86"
            }
        }
        mips {
            ndk {
                abiFilter "mips"
            }
        }
        armv7 {
            ndk {
                abiFilter "armeabi-v7a"
            }
        }
        arm {
            ndk {
                abiFilter "armeabi"
            }
        }
        fat
    }
}

为不同的架构设置不同的版本号(方便google play store上架)

import java.util.regex.Pattern
 
android {
    ...
    defaultConfig{
        versionCode getVersionCodeFromManifest()
    }
    ...
    productFlavors {
        x86 {
            versionCode Integer.parseInt("6" + defaultConfig.versionCode)
            ndk {
                abiFilter "x86"
            }
        }
        mips {
            versionCode Integer.parseInt("4" + defaultConfig.versionCode)
            ndk {
                abiFilter "mips"
            }
        }
        armv7 {
            versionCode Integer.parseInt("2" + defaultConfig.versionCode)
            ndk {
                abiFilter "armeabi-v7a"
            }
        }
        arm {
            versionCode Integer.parseInt("1" + defaultConfig.versionCode)
            ndk {
                abiFilter "armeabi"
            }
        }
        fat
    }


}
 
def getVersionCodeFromManifest() {
    def manifestFile = file(android.sourceSets.main.manifest.srcFile)
    def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
    def matcher = pattern.matcher(manifestFile.getText())
    matcher.find()
    return Integer.parseInt(matcher.group(1))
}


决定是否调用ndk-build
如果在你的工程目录下面有jni/目录, 那么build系统将会试图 调用 ndk-build来自动生成.so文件
当前的实现是: 忽略你jni目录下的Android.mk这个makefile,然后自动创建一个新的
对于简单的工程来说,这真是太方便了(你根本就不需要自己创建*mk文件)
但是对于大工程来说,这就有点令人烦恼了, 因为你需要Makefile提供的所有功能. 你可以在build.gradle文件里禁止

android{
    ...
    sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call
}

参考资料

http://www.flakor.cn/2014-02-07-198.html

http://www.nsdev.org/blog/2014/01/01/including-native-libraries-in-your-apk

http://stackoverflow.com/questions/21096819/jni-and-gradle-in-android-studio