顯示具有 Android 標籤的文章。 顯示所有文章
顯示具有 Android 標籤的文章。 顯示所有文章

2017年2月16日 星期四

[Android] UnsatisfiedLinkError

症狀


E/AndroidRuntime: FATAL EXCEPTION: Thread-3056
Process: com.perfectcorp.beautycircle, PID: 14917
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.perfectcorp.beautycircle-1/base.apk"],nativeLibraryDirectories=[/data/app/com.perfectcorp.beautycircle-1/lib/arm64, /data/app/com.perfectcorp.beautycircle-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libweibosdkcore.so"
at java.lang.Runtime.loadLibrary(Runtime.java:367)
at java.lang.System.loadLibrary(System.java:1076)
at com.sina.weibo.sdk.net.HttpManager.(SourceFile:83)
at com.sina.weibo.sdk.net.NetUtils.internalHttpRequest(SourceFile:46)
at com.sina.weibo.sdk.utils.AidTask.loadAidFromNet(SourceFile:344)
at com.sina.weibo.sdk.utils.AidTask.access$3(SourceFile:331)
at com.sina.weibo.sdk.utils.AidTask$2.run(SourceFile:203)
at java.lang.Thread.run(Thread.java:818)

線索

  1. 這問題發生在 Nexus 9 的機器上,HTC Nexus 9 是一台 ARM64 的機器
  2. java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.perfectcorp.beautycircle-1/base.apk"],nativeLibraryDirectories=[/data/app/com.perfectcorp.beautycircle-1/lib/arm64, /data/app/com.perfectcorp.beautycircle-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libweibosdkcore.so"

解法

  • Root cause 在於 Nexus 9 是 ARM64 的機器,但 libweibosdkcore.so 卻只有 x86。
  • 為了解決這問題,解法會是指定 .so 的目錄
  1. 在 app gradle 的 defaultConfig 加上 ndk { abiFilters "armeabi-v7a" }
  2. 在 project的根目錄下,找到 gradle.properties (不存在則新建)"android.useDeprecatedNdk=true 指定目錄",不讓系统自動找.so檔

Reference

  1. http://www.jianshu.com/p/cb05698a1968
  2. https://corbt.com/posts/2015/09/18/mixing-32-and-64bit-dependencies-in-android.html

2011年10月27日 星期四

[Android] expired debug certificate

http://developer.android.com/resources/faq/troubleshooting.html#signingcalendar

I can't compile my app because the build tools generated an expired debug certificate

If your development machine uses a locale that has a non-Gregorian calendar, you may encounter problems when first trying to compile and run your application. Specifically, you may find that the Android build tools won't compile your application because the debug key is expired.
The problem occurs because the Keytool utility — included in the JDK and used by the Android build tools — fails to properly handle non-Gregorian locales and may create validity dates that are in the past. That is, it may generate a debug key that is already expired, which results in the compile error.
If you encounter this problem, follow these steps to work around it:
  1. First, delete the debug keystore/key already generated by the Android build tools. Specifically, delete the debug.keystore file. On Linux/Mac OSX, the file is stored in ~/.android. On Windows XP, the file is stored in C:\Documents and Settings\\.android. On Windows Vista, the file is stored in C:\Users\\.android
  2. Next, you can either
    • Temporarily change your development machine's locale (date and time) to one that uses a Gregorian calendar, for example, United States. Once the locale is changed, use the Android build tools to compile and install your app. The build tools will regenerate a new keystore and debug key with valid dates. Once the new debug key is generated, you can reset your development machine to the original locale.
    • Alternatively, if you do not want to change your machine's locale settings, you can generate the keystore/key on any machine using the Gregorian calendar, then copy the debug.keystore file from that computer to the proper location on your development machine.
This problem has been verified on Windows and may apply to other platforms.
For general information about signing Android applications, see Signing Your Applications.

2011年9月28日 星期三

[Android] Dynamically change Options Menu Items in Android

Sometimes, we need to change options menu dynamically, you can follow this example.
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		menu.clear();
		// You can use mEnableMenu to enable meun
		if (mEnableMenu)
		{
			menu.add(0, 0, 0, "Setting");
		}
		return super.onPrepareOptionsMenu(menu);
	}

Reference: 

[Android] How to monitor sdcard mount/unmout?

In this example, we demo the sdcard mount/unmount monitor.
	public void onCreate() {
		super.onCreate();
		this.registerReceiver(this.myReceiver, getSDCardIntentFilter());
	}

	public IntentFilter getSDCardIntentFilter() {
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
		intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
		intentFilter.addDataScheme("file");
		return intentFilter;
	}

	private BroadcastReceiver myReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) 
		{
			String action = intent.getAction();
			if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED))
			{
			  // handle SD card unmounted
			}
			else if (action.equals(Intent.ACTION_MEDIA_MOUNTED))
			{
			  // handle SD card mounted
			}
		}
	};

[Android] How to block back and search button on Dialog?

It will cancel dialog If user press back or search button on dialog. Therefore, sometimes we need to block back and search button on dialog to ensure the logic of dialog can work normally.
		public void alertDialog(final String title, final String msg, final String btn1, final String btn2, final String btn3, final String cmd, final String data)
		{
			AlertDialog.Builder MyAlertDialog = new AlertDialog.Builder(self);
			MyAlertDialog.setIcon(android.R.drawable.ic_dialog_alert);
			MyAlertDialog.setTitle(title);
			MyAlertDialog.setMessage(msg);
			MyAlertDialog.setCancelable(false); // block back button

			MyAlertDialog.setOnKeyListener(new DialogInterface.OnKeyListener()
			{
				@Override
				public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
				{
					if (keyCode==KeyEvent.KEYCODE_SEARCH)	// block search button
						return true;
					else
						return false;
				}
			});

			DialogInterface.OnClickListener OkClick = new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
				// Handle onClick
				}
			};
			if (btn1 != null)
				MyAlertDialog.setPositiveButton(btn1, OkClick );
			if (btn2 != null)
				MyAlertDialog.setNegativeButton(btn2, OkClick );
			if (btn3 != null)
				MyAlertDialog.setNeutralButton(btn3, OkClick );

			 MyAlertDialog.show();
		}

[Android] How to create status bar notification?

To create a status bar notification:


1. Get a reference to the NotificationManager:
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);

2. Instantiate the Notification:
int icon = R.drawable.notification_icon;
CharSequence tickerText = "Hello";
long when = System.currentTimeMillis();

Notification notification = new Notification(icon, tickerText, when);

3. Define the Notification's expanded message and Intent:
Context context = getApplicationContext();
CharSequence contentTitle = "My notification";
CharSequence contentText = "Hello World!";
Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

4. Pass the Notification to the NotificationManager:
private static final int HELLO_ID = 1;

mNotificationManager.notify(HELLO_ID, notification);
That's it. Your user has now been notified.

Reference:

2011年9月16日 星期五

[Android] How can I refresh MediaStore on Android?

Sometimes, we cannot get the correct information from MediaStore after file add or delete. We need to do MediaStore refresh to make MediaStore parsing the information instantly.
package com.roryok.MediaRescan;

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;

public class MediaRescan extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                Uri.parse("file://" + Environment.getExternalStorageDirectory()))); 
        setContentView(R.layout.main);
    }

    //Rescan the sdcard after copy the file
    private void rescanSdcard() throws Exception{     
      Intent scanIntent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                Uri.parse("file://" + Environment.getExternalStorageDirectory()));   
      IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
      intentFilter.addDataScheme("file");     
      sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                Uri.parse("file://" + Environment.getExternalStorageDirectory())));    
    }
}


Reference:

2011年5月19日 星期四

[Android] How to set hostname on Android device?

$ su
# setprop net.hostname whateveryouwant

Please restart wifi after set hostname.

You can use the following code to check if it works.
InetAddress addr = InetAddress.getByName("192.168.15.105"); // 192.168.15.105 is your device ip
Reference:

2011年5月17日 星期二

[Android] tcpdump for Android devices

tcpdump for Android devices

  • tcpdump, a powerful command-line packet analyzer.
How to install on your device?
Unzip tcpdump.rar
// copy to tcpdump to your /system/bin:
1. adb remount 
2. adb push tcpdump /system/bin
3. adb shell chmod 777 /system/bin/tcpdump 

How to run tcpdump on your device?
Run: adb shell tcpdump -i any -w /storage/netlog.cap
Stop: USE Ctrl + C to abort this packet monitor. 
Download log: adb pull /storage/netlog.cap c:\ 
Delete log: adb rm /storage/netlog.cap 

tcpdump parameters
-i :後面接要監聽的網路介面,例如 eth0, lo, ppp0 等等的介面;any表示所有的網路介面
-w :後面接要監聽所得的封包資料儲存的檔名


References

2011年5月5日 星期四

[Android] SharedPreferences - a simple example

我們可以用Android的SharedPreferences來讀寫裝置上的偏好設定檔。

首先需要使用getSharedPreferences(String, int)來取得SharedPreferences。
  • 第一個參數是偏好設定檔的名稱,如果指定的設定檔名稱不存在的話,當使用者使用SharedPreferences.edit()而且Editor.commit()去更新資料時該偏好設定檔會自動產生。
  • 第二個參數是偏好設定檔的模式,0或MODE_PRIVATE是預設的參數,MODE_WORLD_READABLE 和MODE_WORLD_WRITEABLE為設定控制權限,MODE_MULTI_PROCESS為多個程序可以共用的偏好設定檔。

String PREFS_FILENAME = "APP_SETTING";
SharedPreferences mConfig = getSharedPreferences(PREFS_FILENAME, 0);
SharedPreferences.Editor mConfigEditor = mConfig.edit();

mConfigEditor.putBoolean("Setting1", true);        // set Setting2 = true
mConfigEditor.commit();                  // We do commit to save data
boolean bSetting1 = mConfig.getBoolean("Setting1", false);// get Setting1 from SharedPreferences
System.out.println("Vince bSetting1 = " + bSetting1);   // bSetting = true

mConfigEditor.putBoolean("Setting2", true);        // set Setting2 = true
boolean bSetting2 = mConfig.getBoolean("Setting2", false);// get Setting2 from SharedPreferences
System.out.println("Vince bSetting2 = " + bSetting2);   // bSetting = false


References:

2011年4月27日 星期三

[Android] Build NDK Environment

NDK是什麼?
The Android NDK is a toolset that lets you embed components that make use of native code in your Android applications.

安裝NDK

Step 1:安裝Cygwin
  • 下載點:到http://cygwin.com/install.html下載setup.exe
  • Cygwin Setup: Next
  • Cygwin Setup - Choose Installation Type: Install from Internet > Next
  • Cygwin Setup - Choose Installation Directory: All Users > Next
  • Cygwin Setup - Select Local Package Directory > Next
  • Cygwin Setup - Select Connection Type: Direct Connection > Next
  • Cygwin Setup - Choose Download Site(s): ftp:/ftp.ntu.edu.tw > Next
  • Cygwin Setup - Select Packages as below > Next


  • Launch Cygwin and run sure "gcc -v" and "make -v" to verify if it works correctly.
$ gcc -v
Reading specs from /usr/lib/gcc/i686-pc-cygwin/3.4.4/specs
Configured with: /managed/gcc-build/final-v3-bootstrap/gcc-3.4.4-999/configure --verbose --program-suffix=-3 --prefix=/usr --exec-prefix=/us
r --sysconfdir=/etc --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-languages=c,ada,c++,d
,f77,pascal,java,objc --enable-nls --without-included-gettext --enable-version-specific-runtime-libs --without-x --enable-libgcj --disable-j
ava-awt --with-system-zlib --enable-interpreter --disable-libgcj-debug --enable-threads=posix --enable-java-gc=boehm --disable-win32-registr
y --enable-sjlj-exceptions --enable-hash-synchronization --enable-libstdcxx-debug
Thread model: posix
gcc version 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)

$ make -v
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for i686-pc-cygwin

Step 2:安裝NDK

  • 下載點:到http://developer.android.com/sdk/ndk/index.html 下載Android NDK for Windows
  • Cygwin Setup: Next
  • Cygwin Setup - Choose Installation Type: Install from Internet > Next
  • Cygwin Setup - Choose Installation Directory: All Users > Next
  • Cygwin Setup - Select Local Package Directory > Next
  • Cygwin Setup - Select Connection Type: Direct Connection > Next
  • Cygwin Setup - Choose Download Site(s): ftp:/ftp.ntu.edu.tw > Next
  • Cygwin Setup - Select Packages as below > Next
  • 然後到C:\cygwin\home\"使用者名稱" 目錄下,用windows編輯器編輯.bash_profile :
在最後一行後加入下面四行,然後存檔:
PATH=/android-ndk-r5:${PATH}
NDK_ROOT=/android-ndk-r5
NDK_Sample=/android-ndk-r5/samples
export PATH NDK_ROOT NDK_Sample
(NDK_Sample是指定你的JNI程式所要放置的目錄,這邊我我指定到/android-ndk-r5/samples,這個參數會在Eclipse的C編譯中使用到。)
  • 開啟Cygwin Bash Shell到/android-ndk-r5/samples/hello-jni/目錄下,執行ndk-build
vince_huang@VinceH-DT ~
$ cd /android-ndk-r5/samples/hello-jni/

vince_huang@VinceH-DT /android-ndk-r5/samples/hello-jni
$ ndk-build
Gdbserver      : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup       : libs/armeabi/gdb.setup
Install        : libhello-jni.so => libs/armeabi/libhello-jni.so

Step 2:安裝Eclipse CDT(C/C++ Development Tool)

  • Eclipse Help > Install New Software... > Add...
  • Name: CDT
  • Location: http://download.eclipse.org/tools/cdt/releases/galileo
  • 安裝Eclipse C/C++ Development Tools
  • 安裝CDT GNU Toolchain Build Support
  • 安裝CDT GNU Toolchain Debug Support
  • 安裝CDT Utilities
  • 安裝Eclipse C/C++ Development Platform
  • 安裝完成後,Eclipse會要求重新開啟Eclipse,重開Eclipse後便可以在Eclipse中編輯C/C++程式。

相關連結

[Android] MediaStore

The Media provider contains meta data for all available media on both internal and external storage devices.

A Example:

String[] projection = new String[] {
  Images.ImageColumns._ID,
  Images.ImageColumns.BUCKET_DISPLAY_NAME,
  Images.ImageColumns.BUCKET_ID,
  Images.ImageColumns.DATE_TAKEN,
  Images.ImageColumns.DESCRIPTION,
  Images.ImageColumns.IS_PRIVATE,
  Images.ImageColumns.LATITUDE,
  Images.ImageColumns.LONGITUDE,
  Images.ImageColumns.MINI_THUMB_MAGIC,
  Images.ImageColumns.ORIENTATION,
  Images.ImageColumns.PICASA_ID,
  Images.ImageColumns.DATA,
  Images.ImageColumns.DATE_ADDED,
  Images.ImageColumns.DATE_MODIFIED,
  Images.ImageColumns.DISPLAY_NAME,
  Images.ImageColumns.MIME_TYPE,
  Images.ImageColumns.SIZE,
  Images.ImageColumns.TITLE
};

// We can use the following query method to query data.
//Cursor cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);

Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);

int count = cursor.getCount();

System.out.println("count = " + count);

for (int i=0; i<count; i++)
{
  cursor.moveToPosition(i);
  int columnSize = cursor.getColumnCount();
  for (int j=0; j<columnsize; j++)
  {
    System.out.println("index = " + j + " " + cursor.getColumnName(j) + " = " + cursor.getString(j));
  }
}

Reference:

2011年3月9日 星期三

[Android] Basic tips for Android protection

Vince筆記
  • Afirewall- aFirewall is the free version of Android Firewall which blocks incoming calls from numbers you specify. If you don’t wants to receive a particular call from a person then install this superb application. This application works fantastically and is a must have for anyone who wants to block incoming calls from telemarketers, bill collectors or your run of the mill harassing calls.
    • 延伸閱讀
      • Reference 1
  • Droidhunter- it is a security service for Android with inbuilt anti-virus, interruption detection, online managing service and incoming alerts. This app is similar to the traditional desktop security software except that it looks for Android specific threats. Android phone scans as “Very Clean” but user reviews report the interception of suspicious apps as well as other Android security threats. This program comes in a free version as well as a paid version which can be used to locate a lost or stolen phone.
    • 延伸閱讀
      • Reference 1
  • App protector- it is an application which allows you to lock your choice of applications on your Android phone. You can set a password, and when you try to enter a protected app, you will be asked to enter the password. Useful if your phone is stolen or if you store information on your phone you’d rather not have accessed by people you let look at your Android phone. Protect as many or as few applications as you like and remove the password lock on applications at anytime. One thing to keep in mind is you will want to protect your uninstaller application, so a person can’t simply uninstall this program to remove the password locks and then access your sensitive information. This program comes in a free time-limited trial (7 days). You can remove the password anytime, but a person can’t simply remove the password locks by uninstalling the application.
    • 延伸閱讀
      • Reference 1

相關連結





      2011年3月8日 星期二

      [Android] Logcat in Java

      在Eclipse中叫出LogCat的小視窗
      Window -> Show View -> Other,找Android -> LogCat

      Log基本用法
      Log.v(TAG, Message): 詳細訊息(Verbose)
      Log.d(TAG, Message): 除錯訊息(Debug)
      Log.i(TAG, Message): 資訊訊息(INFO)
      Log.w(TAG, Message): 警告訊息(Warning)
      Log.e(TAG, Message): 錯誤訊息(Error)
      
      第一個欄位是TAG,第二個欄位是Message,TAG可以方便做filter
      Ex: Log.i("Tag", "This log.i for logcat");
      

      如何filter
      1. 在Eclipse中叫出LogCat的小視窗
      2. 按下綠色加號產生filter






      3. 設定filter

      [Android] Android Service

      什麼是Android Service
      • A service can run in the background to perform work even while the user is in a different application.
      • A service can allow other components to bind to it, in order to interact with it and perform interprocess communication.
      • A service runs in the main thread of the application that hosts it, by default.

      來個例子

      在AndroidManifest.xml中增加一個Service,其中ExampleService為你Service class的名稱
      <manifest ... >
        ...  
        <application ... >      
        <service android:name=".ExampleService" />
        ...  
       </application>
      </manifest>

      建立myService.java,參考自Android學習筆記 - 背景執行服務(Service)
      package com.cyberlink.dtcpip; 
      import android.app.Service; 
      import android.content.Intent; 
      import android.os.Handler; 
      import android.os.IBinder; 
      import android.util.Log; 
      import java.util.Date; 
      
      //繼承android.app.Service 
      public class myService extends Service { 
          private Handler handler = new Handler(); 
        
          @Override
          public IBinder onBind(Intent intent) { 
              return null; 
          } 
        
          @Override
          // The old onStart method that will be called on the pre-2.0
          // platform. On 2.0 or later we override onStartCommand().
          public int onStartCommand(Intent intent, int flags, int startId) { 
          // We want this service to continue running until it is explicitly
          // stopped, so return sticky.
              handler.postDelayed(showTime, 1000); 
              super.onStartCommand(intent, flags, startId);
              return START_STICKY;
          } 
        
          @Override
          public void onDestroy() { 
              handler.removeCallbacks(showTime); 
              super.onDestroy(); 
          } 
            
          private Runnable showTime = new Runnable() { 
              public void run() { 
                  //log目前時間 
                  Log.i("[MyService]", "Time = " + new Date().toString()); 
                  handler.postDelayed(this, 1000); 
              } 
          }; 
      }
      

      By returning the START_STICKY constant when your Service is started, you tell Android that if it has to kill the Service to free up valuable resources, then you’d like it to restart the Service when resource become available again.

      啟動Service
      Intent intent = new Intent(this, myService.class); 
      startService(intent); 
      

      關閉Service
      Intent intent = new Intent(this, myService.class); 
      stopService(intent); 
      

      相關連結

      2011年3月4日 星期五

      [Android] Android Debug Bridge (ADB)

      http://developer.android.com/guide/developing/tools/adb.html

      You can find the adb tool in <sdk>/platform-tools/.

      Installing an Application
      adb install 
      

      Copying Files to or from an Emulator/Device Instance
      To copy a file or directory (recursively) from the emulator or device, use
      adb pull <remote>> <local>

      To copy a file or directory (recursively) from the emulator or device, use
      adb push <local> <remote>

      2011年3月3日 星期四

      [Android] undefined reference to in ndk-build

      在ndk-build時, 需要的static library都有定義到

      LOCAL_STATIC_LIBRARIES := libABC \
      LOCAL_STATIC_LIBRARIES += libCDE \
      

      但卻還是發生undefined reference to `xxxxxx' 時

      這時候可能是 static library 的順序不對, 改一下順序再試看看吧
      LOCAL_STATIC_LIBRARIES := libCDE \
      LOCAL_STATIC_LIBRARIES += libABC \
      

      [Android] System process cannot write file to the /sdcard

      http://groups.google.com/group/android-platform/browse_thread/thread/065bc037e44af858

      The system process is explicitly forbidden to open files on SD because those devices are frequently removable during runtime.

      If a process holds open file descriptors when the file system is unmounted, that process will be killed by the kernel.

      In the case of the system process, that would bring down the whole Android runtime; hence the restriction.

      Applications resident on SD complicate this a bit, but that's the basic problem in a nutshell, and is the reason for the explicit policy that you're seeing in action.

      For your test suite, you might consider using a two-process architecture in which the system-process test code communicates via the Binder or other IPC mechanism with another process whose job is mostly to handle the file I/O.

      Note that system process can write file to the /data.

      [Android] logcat in JNI

      http://blog.xuite.net/xoanonlin/LoveFor/39052853

      1. 加入header檔

      #include <android log.h>
      

      2. 加入需要的定義
      #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , "liblog",__VA_ARGS__) 
      
      #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "liblog",__VA_ARGS__)
      
      #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "liblog",__VA_ARGS__)
      
      #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "liblog",__VA_ARGS__)
      
      #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "liblog",__VA_ARGS__)
      

      3. 用法
      LOGD("Debug Message is from JNI");
      

      4. 加入LOCAL_LDLIBS在Android.mk, 這行要在include $(BUILD_SHARED_LIBRARY)之前, 否則會有undefined reference的錯誤
      LOCAL_LDLIBS :=  -L$(SYSROOT)/usr/lib -llog
      

      2011年3月2日 星期三

      [Android] wildcard usage in Android.mk

      For example, you can use wildcard to enumerate *.c in the folder.

      MY_NativeC_FILES += $(wildcard $(LOCAL_PATH)/*.c)