2012-04-16

Intent呼叫第三方程式 (setComponent)

Intent呼叫第三方程式 (setComponent)


只要利用adb logcat ,再搭配使用setComponet(),就可以輕易的呼叫第三方程式(不在自己的application內)。
詳細用法參考原文:
http://developer.android.com/reference/android/content/Intent.html#setComponent%28android.content.ComponentName%29

比如我自己的程式想執行Android裡面的Settings,先用adb logcat看系統是如何呼叫Settings的

I/ActivityManager(   60): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.settings/.Settings }
I/ActivityManager(   60): Displayed activity com.android.settings/.Settings: 1205 ms (total 1205 ms)

只要有這個cmp就可以呼叫Settings了

public class Main extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent i = new Intent();
        ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings");
        i.setComponent(comp);
        startActivity(i);   
    }
}

另外有些程式要被執行,Intent還要多加搭配Intent.setData()或者是Intent.setAction()等方式。比如:想要開啟Browser,而且是開啟tw.yahoo.com的網頁,程式碼如下:
        Intent i = new Intent();
        ComponentName comp = new ComponentName("com.android.browser", "com.android.browser.BrowserActivity");
        i.setComponent(comp);
        Uri uri = Uri.parse("http://tw.yahoo.com");
        i.setData(uri);
        startActivity(i);

出處
http://blog.xuite.net/ffc99a3b/ooxx/45834263-%5B%E8%BD%89%E8%BC%89%5D+Intent%E5%91%BC%E5%8F%AB%E7%AC%AC%E4%B8%89%E6%96%B9%E7%A8%8B%E5%BC%8F+(setComponent)+

2012-04-02

receiver


可以在代码文件中声明一个receiver,也可以在manifest中声明一个,前者中的receiver只有在该activity launch起来以后才会监听其所感兴趣的文件,而如果在androidManifext.xml中声明的话,就不受限制,随时可以监听感兴趣的事件。

首先谈谈在androidManifext.xml中注册一个receiver, 例如我们想监听相机按钮按下事件的发生,并且发生后调用我们的camera程序
  1. <receiver android:name="CameraButtonIntentReceiver">
  2.             <intent-filter>
  3.                 <action android:name="android.intent.action.CAMERA_BUTTON"/>
  4.             </intent-filter>
  5. </receiver>
复制代码
在这个配置文件中声明了一个receiver用来监听相机的按键事件,所以还需要在代码文件定义与配置文件中同名的receiver:
  1. public class CameraButtonIntentReceiver extends BroadcastReceiver {
  2. public void onReceive(Context context, Intent intent) {
  3.     Intent i = new Intent(Intent.ACTION_MAIN);
  4.         i.setClass(context, Camera.class);
  5.         i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  6.         context.startActivity(i);
  7. }
  8. }
复制代码

关于另外一种,在代码中注册一个receiver,例如我们想在代码文件中监听电池电量的变化,就可以按照如下方法
  1. private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
  2.       @Override
  3.         public void onReceive(Context context, Intent intent) {
  4.             String action = intent.getAction();
  5.               if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
  6.               …
  7.               }
  8.         }
  9. }
复制代码
这种方法需要在onCreate 或者onStart中注册该receiver所感兴趣的intent,如:
  1. registerReceiver(mBatteryReceiver, Intent.ACTION_BATTERY_CHANGED);
复制代码
在onStop及onDestroy中注销监听
  1. registerReceiver(mBatteryReceiver, Intent.ACTION_BATTERY_CHANGED);
复制代码

[Android] Incorporating Google AdMob Ads into your app

Incorporating Google AdMob Ads into your app is a three step process:
  1. Add the SDK JAR to your Eclipse project.
  2. Declare com.google.ads.AdActivity in AndroidManifest.xml.
  3. Set up required network permissions in the manifest.

Adding the SDK JAR

The decompressed SDK consists of a JAR, a javadoc folder and a README.
1. Right click on your app project in Eclipse and choose Properties.
2. Select Java Build Path and the Libraries tab. Then click Add External JARs... to add the Google AdMob Ads JAR.

com.google.ads.AdActivity

The AdMob Ads SDK requires that com.google.ads.AdActivity be declared in your app's AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
          package="com.company" 
          android:versionCode="1" android:versionName="1.0"> 
  <application android:icon="@drawable/icon" android:label="@string/app_name" 
               android:debuggable="true"> 
    <activity android:label="@string/app_name" android:name="BannerExample"> 
      <intent-filter> 
        <action android:name="android.intent.action.MAIN"/> 
        <category android:name="android.intent.category.LAUNCHER"/> 
      </intent-filter> 
    </activity> 
    <activity android:name="com.google.ads.AdActivity" 
              android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/> 
  </application> </manifest>

Permissions

Making ad requests requires the networking permissions INTERNET and ACCESS_NETWORK_STATE, so these must also be declared in the manifest:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
          package="com.company" 
          android:versionCode="1" android:versionName="1.0"> 
  <application android:icon="@drawable/icon" android:label="@string/app_name" 
               android:debuggable="true"> 
    <activity android:label="@string/app_name" android:name="BannerExample"> 
      <intent-filter> 
        <action android:name="android.intent.action.MAIN"/> 
        <category android:name="android.intent.category.LAUNCHER"/> 
      </intent-filter> 
    </activity> 
    <activity android:name="com.google.ads.AdActivity" 
              android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/> 
  </application> 
  <uses-permission android:name="android.permission.INTERNET"/> 
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> </manifest>
You should now be able to rebuild your project without any errors.

Adding a com.google.ads.AdView

Android apps are composed of View objects, Java instances the user sees as text areas, buttons and other controls. AdView is simply another View subclass displaying small HTML5 ads that respond to user touch.
Like any View, an AdView may be created either purely in code or largely in XML.
The five lines of code it takes to add a banner:
  • Import com.google.ads.*
  • Declare an AdView instance
  • Create it, specifying a unit ID—your AdMob publisher ID
  • Add the view to the UI
  • Load it with an ad
The easiest place to do all this is in your app’s Activity.
import com.google.ads.*; 
 public class BannerExample extends Activity { 
  private AdView adView; 
 
  @Override 
  public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
 
    // Create the adView 
    adView = new AdView(this, AdSize.BANNER, MY_AD_UNIT_ID); 
 
    // Lookup your LinearLayout assuming it’s been given 
    // the attribute android:id="@+id/mainLayout" 
    LinearLayout layout = (LinearLayout)findViewById(R.id.mainLayout); 
 
    // Add the adView to it 
    layout.addView(adView); 
 
    // Initiate a generic request to load it with an ad 
    adView.loadAd(new AdRequest()); 
  } 
 
  @Override 
  public void onDestroy() { 
    if (adView != null) { 
      adView.destroy(); 
    } 
    super.onDestroy(); 
  } }
Warning: Make sure you're in test mode during development to avoid being disabled for clicking your own ads. See the Best Practices guide for more details on enabling test ads.
You can download an example project containing this code here and may alternately create your banner in XML.

The Result

When you now run your app you should see a banner at the top of the screen:

Note: The very first time AdMob sees your publisher ID it may take up to two minutes to receive an ad. This initial two minute lag will recur every time the ID goes unused for 24 hours.
Warning: All new Android apps created after October 14, 2011 will require an AdMob SDK that was released on or after March 15, 2011. This corresponds to version 4.0.2+ for Android. If you downloaded the library from our official download site, then you're already set. Otherwise you may have an old version of the AdMob SDK that was released prior to March 15, 2011, and your new app will not receive any ad impressions until you update your SDK.




source:

2012-03-29

ComponentName

Android软件时,常常需要打开系统设置或信息界面,来设置相关系统项或查看系统的相关信息,这时我们就可以使用以下语句来实现:(如打开“无线和网络设置”界面)

  Intent intent = new Intent("/");
  ComponentName cm = new ComponentName("com.android.settings","com.android.settings.WirelessSettings");
  intent.setComponent(cm);
  intent.setAction("android.intent.action.VIEW");
  activity.startActivityForResult( intent , 0);
  经过测试,使用下面字段可以在软件中直接打开相应的系统界面
  com.android.settings.AccessibilitySettings 辅助功能设置
  com.android.settings.ActivityPicker 选择活动
  com.android.settings.ApnSettings APN设置
  com.android.settings.ApplicationSettings 应用程序设置
  com.android.settings.BandMode 设置GSM/UMTS波段
  com.android.settings.BatteryInfo 电池信息
  com.android.settings.DateTimeSettings 日期和坝上旅游网时间设置
  com.android.settings.DateTimeSettingsSetupWizard 日期和时间设置
  com.android.settings.DevelopmentSettings 应用程序设置=》开发设置
  com.android.settings.DeviceAdminSettings 设备管理器
  com.android.settings.DeviceInfoSettings 关于手机
  com.android.settings.Display 显示——设置显示字体大小及预览
  com.android.settings.DisplaySettings 显示设置
  com.android.settings.DockSettings 底座设置
  com.android.settings.IccLockSettings SIM卡锁定设置
  com.android.settings.InstalledAppDetails 语言和键盘设置
  com.android.settings.LanguageSettings 语言和键盘设置
  com.android.settings.LocalePicker 选择手机语言
  com.android.settings.LocalePickerInSetupWizard 选择手机语言
  com.android.settings.ManageApplications 已下载(安装)软件列表
  com.android.settings.MasterClear 恢复出厂设置
  com.android.settings.MediaFormat 格式化手机闪存
  com.android.settings.PhysicalKeyboardSettings 设置键盘
  com.android.settings.PrivacySettings 隐私设置
  com.android.settings.ProxySelector 代理设置
  com.android.settings.RadioInfo 手机信息
  com.android.settings.RunningServices 正在运行的程序(服务)
  com.android.settings.SecuritySettings 位置和安全设置
  com.android.settings.Settings 系统设置
  com.android.settings.SettingsSafetyLegalActivity 安全信息
  com.android.settings.SoundSettings 声音设置
  com.android.settings.TestingSettings 测试——显示手机信息、电池信息、使用情况统计、Wifi information、服务信息
  com.android.settings.TetherSettings 绑定与便携式热点
  com.android.settings.TextToSpeechSettings 文字转语音设置
  com.android.settings.UsageStats 使用情况统计
  com.android.settings.UserDictionarySettings 用户词典
  com.android.settings.VoiceInputOutputSettings 语音输入与输出设置
  com.android.settings.WirelessSettings 无线和网络设置

2012-03-28

[android] java.lang.reflect.Method.invoke()

Ex: Class A

public   Class   A
{
        public   void   setName(String   name)
       {

        }
}

A   a   =   new   A();
Class   c   =   Class.forName( "A ");
Method   method   =   c.getMethod( "setName ",   new   Class[]{String.class});
//從Class c中找到名為setName, 帶一個String參數的methid
method.invoke(a,   new   Object[]{ "name "});
//調用a的該方法, 參數為"name"的String


以上相當於a.setName( "name ");

[Android] Wifi hotspot API -- JAVA Reflection

There is no official API, but you can use reflection to handle it. I know some say, it's not recommended, however imho I say, screw it if Google doesn't want to provide an API for whatever reason.
Below is the code of an activity I used in my application, where the user can enable/disable the Wifi AP. When you enable Wifi AP, usually the regular Wifi will be turned off, so after the user disables the Wifi AP again, we'll be activating regular wifi again.
The code sample below is taken from one of my projects, hope you can get the logic there easily. Let me know if you have further questions.
Code is tested on Nexus One 2.2 (and I think also 2.3) as well as on Samsung Galaxy S (2.2).


package com.myapp.android.activity.wifi;
 
import android.app.ProgressDialog; import android.content.Context; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.WindowManager; import android.widget.Button;
 
import java.lang.reflect.Method;
 
public class WifiAP extends BaseActivity {

   
// boolean mIsWifiEnabled = false;
   
private static final int WIFI_AP_STATE_UNKNOWN = -1;
   
private static final int WIFI_AP_STATE_DISABLING = 0;
   
private static final int WIFI_AP_STATE_DISABLED = 1;
   
private static final int WIFI_AP_STATE_ENABLING = 2;
   
private static final int WIFI_AP_STATE_ENABLED = 3;
   
private static final int WIFI_AP_STATE_FAILED = 4;

   
private final String[] WIFI_STATE_TEXTSTATE = new String[] {
       
"DISABLING","DISABLED","ENABLING","ENABLED","FAILED"
   
};

   
private WifiManager wifi;

   
@Override
   
protected void onCreate(Bundle icicle) {
       
super.onCreate(icicle);
        setContentView
(R.layout.wifi);
        getWindow
().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
               
|WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
               
|WindowManager.LayoutParams.FLAG_DIM_BEHIND
       
);

        wifi
= (WifiManager) getSystemService(Context.WIFI_SERVICE);
   
}

   
@Override
   
public void onResume() {
       
super.onResume();
        updateStatusDisplay
();
   
}

   
public void toggleWifi(View v) {
       
boolean wifiApIsOn = getWifiAPState()==WIFI_AP_STATE_ENABLED || getWifiAPState()==WIFI_AP_STATE_ENABLING;
       
new SetWifiAPTask(!wifiApIsOn,false).execute();
   
}

   
public void close(View v) {
       
boolean wifiApIsOn = getWifiAPState()==WIFI_AP_STATE_ENABLED || getWifiAPState()==WIFI_AP_STATE_ENABLING;
       
if (wifiApIsOn) {
           
new SetWifiAPTask(false,true).execute();
       
} else {
            finish
();
       
}
   
}


   
/**
     * Endable/disable wifi
     * @param enabled
     * @return WifiAP state
     */

   
private int setWifiApEnabled(boolean enabled) {
       
Log.d("WifiAP", "*** setWifiApEnabled CALLED **** " + enabled);
       
if (enabled && wifi.getConnectionInfo() !=null) {
            wifi
.setWifiEnabled(false);
           
try {Thread.sleep(1500);} catch (Exception e) {}
       
}

       
//int duration = Toast.LENGTH_LONG;
       
//String toastText = "MobileAP status: ";
       
int state = WIFI_AP_STATE_UNKNOWN;
       
try {
            wifi
.setWifiEnabled(false);
           
Method method1 = wifi.getClass().getMethod("setWifiApEnabled",
               
WifiConfiguration.class, boolean.class);
            method1
.invoke(wifi, null, enabled); // true
           
Method method2 = wifi.getClass().getMethod("getWifiApState");
            state
= (Integer) method2.invoke(wifi);
       
} catch (Exception e) {
           
Log.e(WIFI_SERVICE, e.getMessage());
           
// toastText += "ERROR " + e.getMessage();
       
}

       
if (!enabled) {
           
int loopMax = 10;
           
while (loopMax>0 && (getWifiAPState()==WIFI_AP_STATE_DISABLING
                   
|| getWifiAPState()==WIFI_AP_STATE_ENABLED
                   
|| getWifiAPState()==WIFI_AP_STATE_FAILED)) {
               
try {Thread.sleep(500);loopMax--;} catch (Exception e) {}
           
}
            wifi
.setWifiEnabled(true);
       
} else if (enabled) {
           
int loopMax = 10;
           
while (loopMax>0 && (getWifiAPState()==WIFI_AP_STATE_ENABLING
                   
|| getWifiAPState()==WIFI_AP_STATE_DISABLED
                   
|| getWifiAPState()==WIFI_AP_STATE_FAILED)) {
               
try {Thread.sleep(500);loopMax--;} catch (Exception e) {}
           
}
       
}

       
return state;
   
}


   
private int getWifiAPState() {
       
int state = WIFI_AP_STATE_UNKNOWN;
       
try {
           
Method method2 = wifi.getClass().getMethod("getWifiApState");
            state
= (Integer) method2.invoke(wifi);
       
} catch (Exception e) {}
       
Log.d("WifiAP", "getWifiAPState.state " + (state==-1?"UNKNOWN":WIFI_STATE_TEXTSTATE[state]));
       
return state;
   
}

   
private void updateStatusDisplay() {

       
if (getWifiAPState()==WIFI_AP_STATE_ENABLED || getWifiAPState()==WIFI_AP_STATE_ENABLING) {
           
((Button)findViewById(R.id.btnWifiToggle)).setText("Turn off");
            findViewById
(R.id.bg).setBackgroundResource(R.drawable.bg_wifi_on);
       
} else {
           
((Button)findViewById(R.id.btnWifiToggle)).setText("Turn on");
            findViewById
(R.id.bg).setBackgroundResource(R.drawable.bg_wifi_off);
       
}

   
}


   
class SetWifiAPTask extends AsyncTask<Void, Void, Void> {

       
boolean mMode;
       
boolean mFinish;

       
public SetWifiAPTask(boolean mode, boolean finish) {
            mMode
= mode;
            mFinish
= finish;
       
}

       
ProgressDialog d = new ProgressDialog(WifiAP.this);

       
@Override
       
protected void onPreExecute() {
           
super.onPreExecute();
            d
.setTitle("Turning WiFi AP " + (mMode?"on":"off") + "...");
            d
.setMessage("...please wait a moment.");
            d
.show();
       
}

       
@Override
       
protected void onPostExecute(Void aVoid) {
           
super.onPostExecute(aVoid);
           
try {d.dismiss();} catch (IllegalArgumentException e) {};
            updateStatusDisplay
();
           
if (mFinish) finish();
       
}

       
@Override
       
protected Void doInBackground(Void... params) {
            setWifiApEnabled
(mMode);
           
return null;
       
}
   
}

 
} 



出處:
http://stackoverflow.com/questions/7048922/android-2-3-wifi-hotspot-api

[Android] Android Tethering API (froyo or above) --JAVA Reflection

Android Tethering API (froyo or above)

ConnectivityManager class has tethering API methods but those methods are not visible in SDK.
But tethering methods exists in ConnectivityManager class and they works properly.
I used java reflection the standard java feature.
For using those methods, do as following.

1. Get ConnectivityManager class
Object obj = getSystemService(Context.CONNECTIVITY_SERVICE);

2. Find "tether" method on the object
Method m = obj.getClass().getDeclaredMethod("tether", String.class);

3. Invoke that method: When invoke the method send tethering interface usually 'usb0'
m.invoke(obj, "usb0")

If you want to turn off the tethering connection, you simply invoke the "untether" method in this way.

I hope this information helps you.
 
 
出處:
http://tjandroid.blogspot.com/2010/12/enabledisable-usb-tethering.html
 

[android] disable/enable data connection -- JAVA Reflection

Starting from 'FROYO' you can use the IConnectivityManager.setMobileDataEnabled() method. It's hidden in API, but can be accessed with reflection. http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/net/ConnectivityManager.java#376
With this method you can change the system setting: 'Settings -> Wireless & network -> Mobile network settings -> Data Enabled'


Code example:
private void setMobileDataEnabled(Context context, boolean enabled) { 
    final ConnectivityManager conman = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 
    final Class conmanClass = Class.forName(conman.getClass().getName()); 
    final Field iConnectivityManagerField = conmanClass.getDeclaredField("mService"); 
    iConnectivityManagerField.setAccessible(true); 
    final Object iConnectivityManager = iConnectivityManagerField.get(conman); 
    final Class iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName()); 
    final Method setMobileDataEnabledMethod = iConnectivityManagerClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE); 
    setMobileDataEnabledMethod.setAccessible(true); 
 
    setMobileDataEnabledMethod.invoke(iConnectivityManager, enabled); } 

 
Also you need the CHANGE_NETWORK_STATE permission.
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> 
Needless to say that this approach might not work in future android versions. But I guess that applications such as '3G watchdog', 'APNdroid' or 'DataLock' work this way.


出處http://stackoverflow.com/questions/3644144/how-to-disable-mobile-data-on-android

JAVA Reflection

  Reflection 是 Java 程式開發語言的特徵之一,它允許營運中的 Java 程式對自身進行檢查,或者說搜尋 Java 類中各成員的名稱並顯示出來。

 
  考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。

DumpMethods.java
import java.lang.reflect.*;

public class DumpMethods {

    public static void main(String args[]) {
        try {
            Class c = Class.forName(args[0]);
            Method m[] = c.getDeclaredMethods();
            for (int i = 0; i < m.length; i++)
                System.out.println(m[i].toString());
        } catch (Throwable e) {
            System.err.println(e);
        }
    }
}
 
按如下語句執行︰
java DumpMethods java.util.Stack
它的結果輸出為︰
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)
這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。
這個程式使用 Class.forName 載入指定的類,然後調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。
java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。
 
 
出處:
http://www.javaworld.com.tw/confluence/display/J2SE/Reflection

2012-02-25

Activity的生命週期


Activity的生命週期



內容說明:Activity的相關應用整理與生命週期說明
資料來源:Android Developers

Activity是讓我們進行某個功能時的畫面,它的產生是採取堆疊制的,越晚出現的畫面放在最上面,一旦最後出現的畫面被關閉,就會開啟倒數第二個畫面,即是我們在操作Android手機時,若按下"返回"鍵,就會回到上一個畫面的意思。

Activity的生命週期如圖所示:

onCreate()
當activity第一次被啟動時呼叫。此時系統會建立activity的畫面,並且可以透過Bundle等物件進行初始化設定(參考「Activity間互相傳值」)。

onStart()
讓activity能被使用者看見。

onResume()
將activity移到堆疊的最上層,讓使用者可以和activity互動。

onPause()
當有簡訊、電話、Toast、AlertDialog等另外一個activity進行時,進入onPause()的狀態,停止對螢幕的存取能力。如果畫面沒有轉移,則回到onResume();若畫面轉移到新的activity時,則進入onStop()。

onStop()
當使用者看不見此activity的畫面時產生。如果這個activity無法透過返回鍵回復,則進入onDestroy();反之則繼續停留在Stop狀態,當使用者返回此頁面時,則進入onRestart()。

onRestart()
重新啟動Stop狀態的activity。

onDestroy()
當activity被銷毀時執行。銷毀的原因有幾種:activity不會再被使用、程式呼叫finish()、系統因記憶體不足而銷毀Stop程序。要確認activity是否被銷毀可使用 isFinishing() 方法。
這些控制生命週期的方法可以在Eclipse選單 Source > Override/Implement Methods 中找到。
在正常情況下,記憶會保存使用者輸入的資料,但是在記憶體不足時,系統會銷毀Stop狀態的activity,此時使用者輸入的資料會消失。此外, 若是系統設定在程式運行中改變(例如螢幕方向、鍵盤狀態和語言),則activity會立即銷毀並重建(執行onDestroy()後接著執行 onCreate())。

因此如果我們寫的程式不希望使用者輸入的資料因為種種原因而消失,則必需要在onPause()的時候將資料儲存起來,並且在onCreate()將資料讀出,重新顯示在畫面上。

2012-02-03

File System和MTD技术

本節介紹File System和MTD技術

一 FS
熟知的FS有ext2,3,4.但是這些都是針對磁盤設備的。而ES中一般的存儲設備為Flash,由於Flash的特殊性:
  • Flash存儲按照Block size進行劃分,而一個BLS一般有幾十K。(對比磁盤的一個簇才512個字節)。這麼大的BLS有什麼壞處呢?很明顯,擦除一個BL就需要花費很長的時間了。
  • 另外,FLASH操作,一次必須針對一個BL,也就是如果我想修改一個字節的話,也必須先擦掉128K。這不是想死嗎?
  • FLASH每個BL擦寫次數是有限的,例如2百萬次?如果每次都操作同一個BL的話,很快這個BL就會死掉。
所以,針對FLASH的特性,整出了一個Journaling Flash File System(JFFS2,第二版)。它的特點就是:
  • 損耗均衡,也就是每次擦寫都不會停留在一個BL上。例如BL1上寫一個文件,那麼再來一個新文件的時候,FS就不會選擇BL1了,而是選擇BL2。這個技術叫weal leveling:損耗均衡
(apt-get install mtd-tools,下載不少好工具,另外,可見往flash設備寫log,可能會導致flash短命喔)
一些偽文件系統:proc/sysfs,systool工具用來分析sysfs。
ES中常用的FS還有ramfs/rootfs/tmpfs,都是基於內存的FS。這個和前面講的initramfs有什麼關係?實際上這些是基於Initramfs的。
這裡要解釋一下幾個比如容易混淆的東西:
  • ram disk:這個是一個基於ram的disk,用來模擬block設備的,所以它是建立在一個虛擬的BLOCK設備上的,至於上面的FS用什麼,無所謂.這樣就引起效率等的問題。畢竟你的read等操作還是要傳遞到驅動的,並且如果該設備上構造了一個EXT2 FS的話,還得有相應的ext2-FS模塊。麻煩..
  • ramfs:這是一個基於內存的FS,而不是一個基於真實設備的FS。ramfs的特點就是用戶可以死勁寫內存,直到把系統內存搞空。
  • 為了控制一下上面這個缺點,引出了tmpfs,可以指定tmpfs的size。(這個又好像和真實設備有點類似,因為真實設備的存儲空間也是有限的)
  • rootfs是一種特殊的ramfs或者tmpfs(看LK中是否啟用了tmpfs),另外,rootfs是不能被umount的
下面介紹一下如何利用mount loop創建一個虛擬的基於文件的BLOCK設備。
  • 先創建一個全是0的文件,利用dd命令:dd if=/dev/zero of=../fstest bs=1024 count=512 這個解釋如下:從if中拷貝數據到of,每次拷貝字節為1024,拷貝總次數為512. 各位可用十六制工具看看,生成的文件裡邊全是0X00
  • 在這個文件中創建FS,mkfs.ext2fs ../fstest。現在,FS就存在於這個文件了。其實FS也就是一些組織結構,例如superblock,inode等信息
  • 如何把這個帶有FS信息的文件掛載呢?其實也就是如何把這個文件當做一個Block device呢?利用mount的loop選項,mount -t ext2 -o loop fstest /tmp/。這樣這個文件就被當做一個虛擬Block設備掛載到tmp了。
二 MTD技術
MTD全稱是Memory Technology Device,內存技術設備?實際上是一個虛擬設備驅動層,類似Virtual File System。它提供標準API給那些操作Raw Flash的device driver。那麼Flash device和普通的Block device的區別是什麼呢?
  • 普通的BLD只有兩種操作:read和write
  • 而Flash Device有三種操作:read,write和erase,另外,還需要一種wear leveling算法來做損耗均衡
這裡要重點指出的是:
SD/MMC卡、CF(Compact Flash)卡、USB Flash等並不是MTD設備,因為這些設備中已經有一個內置的Flash Translation Layer,這個layer處理erase、wear leveling事情了(這個TL應該是固件中支持的)。所以這些設備直接當做普通的Block Device使用
(上面的描述還是沒能說清楚MTD到底是怎麼用的,以後會結合源碼分析一下)

2.1 內核中啟用MTD支持
這個很簡單,make menuconfig的時候打開就行了,有好幾個選項。
圖1 LK中MTD支持的配置選項
其中:
  • MTC_CHAR和MTD_BLOCK用來支持CHAR模式和BLOCK模式讀寫MTD設備。這個和普通的char以及block設備意思一樣
  • 最後兩個是在內核中設置一個MTD test驅動。8192K用來設置總大小,第二個128用來設置block size。就是在內核中搞了一個虛擬的Flash設備,用作測試
ES中又如何配置MTD及其相關的東西呢?
  • 為Flash Disk設置分區(也可以整個Device就一個分區。BTW,我一直沒徹底搞清楚分區到底是想幹什麼,這個可能是歷史原因啦....)
  • 設置Flash的類型以及location。Flash設備分為NOR和NAND,本節最後會簡單介紹下二者的區別。
  • 為Flash芯片選擇合適的driver
  • 為LK配置driver
下面先看看分區的設置
可對Flash分區,這裡有一些稍微重要的內容:如何把Flash分區的信息傳遞給LK呢?有兩種方法:
  • 將整個device的分區情況存在一個BLock中,這樣BootLoader啟動的時候,根據這個BLock中的內容建立相應信息等。好像只有Red Boot支持。所以叫RedBoot Partition Table。另外,LK可以識別這種分區,通過CFI(Command Flash Interface)讀取這個分區的信息。
  • Kernel Command Line Partitioning:通過Kernel啟動的時候傳入參數,不過KL必須配置一下。Command格式如下:
圖2
再看看Driver的Mapping,也就是將MTD和對應的Flash Driver配對...
kernel/drivers/mtd/maps......,以後要分析
Flash芯片本身的Driver呢?
kernel/drivers/mtd/chips,目前比較流行的是CFI接口
三 一些參考資料和補充知識
MTD的本意是:
We're working on a generic Linux subsystem for memory devices, especially Flash devices.
The aim of the system is to make it simple to provide a driver for new hardware, by providing a generic interface between the hardware drivers and the upper layers of the system.
Hardware drivers need to know nothing about the storage formats used, such as FTL, FFS2, etc., but will only need to provide simple routines for read, write and erase. Presentation of the device's contents to the user in an appropriate form will be handled by the upper layers of the system.
MTD overview
MTD subsystem (stands for Memory Technology Devices) provides an abstraction layer for raw flash devices. It makes it possible to use the same API when working with different flash types and technologies, e.g. NAND, OneNAND, NOR, AG-AND, ECC'd NOR, etc.
MTD subsystem does not deal with block devices like MMC, eMMC, SD, CompactFlash, etc. These devices are not raw flashes but they have a Flash Translation layer inside, which makes them look like block devices. These devices are the subject of the Linux block subsystem, not MTD. Please, refer to this FAQ section for a short list of the main differences between block and MTD devices. And the raw flash vs. FTL devices UBIFS section discusses this in more details.
MTD subsystem has the following interfaces.
  • MTD character devices - usually referred to as /dev/mtd0, /dev/mtd1, and so on. These character devices provide I/O access to the raw flash. They support a number of ioctl calls for erasing eraseblocks, marking them as bad or checking if an eraseblock is bad, getting information about MTD devices, etc. /dev/mtdx竟然是char device!!
  • The sysfs interface is relatively newer and it provides full information about each MTD device in the system. This interface is easily extensible and developers are encouraged to use the sysfs interface instead of older ioctl or /proc/mtd interfaces, when possible.
  • The /proc/mtd proc file system file provides general MTD information. This is a legacy interface and the sysfs interface provides more information.
    MTD subsystem supports bare NAND flashes with software and hardware ECC, OneNAND flashes, CFI (Common Flash Interface) NOR flashes, and other flash types.
Additionally, MTD supports legacy FTL/NFTL "translation layers", M-Systems' DiskOnChip 2000 and Millennium chips, and PCMCIA flashes (pcmciamtd driver). But the corresponding drivers are very old and not maintained very much.
MTD Block Driver:
The mtdblock driver available in the MTD is an archaic tool which emulates block devices on top of MTD devices. It does not even have bad eraseblock handling, so it is not really usable with NAND flashes. And it works by caching a whole flash erase block in RAM, modifying it as requested, then erasing the whole block and writing back the modified. This means that mtdblock does not try to do any optimizations, and that you will lose lots of data in case of power cuts. And last, but not least, mtdblock does not do any wear-leveling.
Often people consider mtdblock as general FTL layer and try to use block-based file systems on top of bare flashes using mtdblock. This is wrong in most cases. In other words, please, do not use mtdblock unless you know exactly what you are doing.There is also a read-only version of this driver which doesn't have the capacity to do the caching and erase/writeback, mainly for use with uCLinux where the extra RAM requirement was considered too large
These are the modules which provide interfaces that can be used directly from userspace. The user modules currently planned include:
  • Raw character access: A character device which allows direct access to the underlying memory. Useful for creating filesystems on the devices, before using some of the translation drivers below, or for raw storage on infrequently-changed flash, or RAM devices.
  • Raw block access :A block device driver which allows you to pretend that the flash is a normal device with sensible sector size. It actually works by caching a whole flash erase block in RAM, modifying it as requested, then erasing the whole block and writing back the modified data.This allows you to use normal filesystems on flash parts. Obviously it's not particularly robust when you are writing to it - you lose a whole erase block's worth of data if your read/modify/erase/rewrite cycle actually goes read/modify/erase/poweroff. But for development, and for setting up filesystems which are actually going to be mounted read-only in production units, it should be fine. There is also a read-only version of this driver which doesn't have the capacity to do the caching and erase/writeback, mainly for use with uCLinux where the extra RAM requirement was considered too large.
  • Flash Translation Layer (FTL):NFTL,Block device drivers which implement an FTL/NFTL filesystem on the underlying memory device. FTL is fully functional. NFTL is currently working for both reading and writing, but could probably do with some more field testing before being used on production systems.
  • Journalling Flash File System, v2:This provides a filesystem directly on the flash, rather than emulating a block device. For more information, see sources.redhat.com.
  • MTD hardware device drivers

    These provide physical access to memory devices, and are not used directly - they are accessed through the user modules above.

    On-board memory:Many PC chipsets are incapable of correctly caching system memory above 64M or 512M. A driver exists which allows you to use this memory with the linux-mtd system.
  • PCMCIA devices:PCMCIA flash (not CompactFlash but real flash) cards are now supported by the pcmciamtd driver in CVS.
  • Common Flash Interface (CFI) onboard NOR flash:This is a common solution and is well-tested and supported, most often using JFFS2 or cramfs file systems.
  • Onboard NAND flash:NAND flash is rapidly overtaking NOR flash due to its larger size and lower cost; JFFS2 support for NAND flash is approaching production quality.
  • M-Systems' DiskOnChip 2000 and Millennium:The DiskOnChip 2000, Millennium and Millennium Plus devices should be fully supported, using their native NFTL and INFTL 'translation layers'. Support for JFFS2 on DiskOnChip 2000 and Millennium is also operational although lacking proper support for bad block handling.
這裡牽扯到NOR和NAND,那麼二者有啥區別呢?
Beside the different silicon cell design, the most important difference between NAND and NOR Flash is the bus interface. NOR Flash is connected to a address / data bus direct like other memory devices as SRAM etc. NAND Flash uses a multiplexed I/O Interface with some additional control pins. NAND flash is a sequential access device appropriate for mass storage applications, while NOR flash is a random access device appropriate for code storage application. NOR Flash can be used for code storage and code execution. Code stored on NAND Flash can't be executed from there. It must be loaded into RAM memory and executed from there.
  • NOR可以直接和CPU相連,就好像內存一樣。NAND不可以,因為NAND還需要別的一些I/O控制接口。所以NAND更像磁盤,而NOR更像內存
  • NOR比NAND貴,並且,NAND支持順序讀取,而NOR支持隨機讀取。
  • 所以,NOR中可存儲代碼,這樣CPU直接讀取就在其中運行。NAND不可以(主要還是因為CPU取地址的時候不能直接找到NAND)

Polling, Interrupt-driven I/O, DMA(Direct Memory Access)

三種將資料在I/O間傳送的方法有
1. Polling
2. Interrupt-driven I/O
3. DMA(Direct Memory Access)

Polling:最簡單的方式讓I/O device與CPU溝通。I/O device只要將information放進status register,CPU會週期性的檢查並取得information來得知需要服務的device。

Interrupt-driven I/O:利用interrupt的機制,當一個I/O device需要服務時,會發出interrupt來通知CPU。一個I/O interrupt對於指令的執行是asynchronous,Control unit需要在執行一個新指令時檢查是否有pending I/O interrupt。I/O interrupt也會傳達更多訊息,如哪個device發出的interrupt和它的priority。

DMA(Direct Memory Access):提供一個device controller,讓I/O device能夠直接在記憶體做資料的傳輸,不需要CPU的參與。

DMA transfer的三個步驟
1. CPU要初始化DMA controller,包括提供I/O device的名稱、執行何種運算、記憶體位置以及所要傳送的bytes數。

2. DMA開始運作。如果request需要多於一次的transfer在bus上,DMA unit會產生下一個memory address和初始下一次的transfer。

3. 一旦DMA transfer完成,DMA controller發出一個interrupt給CPU。


優缺點比較
 Polling  Interrupt-driven I/O DMA 
優點 簡單容易執行,可利用軟體來更改CPU polling的順序 不用浪費許多時間在polling上面 適用於高速裝置
不用浪費許多時間在polling上面 
缺點 因為CPU速度遠快於I/O device,會浪費許多時間在polling上
不適合high-bandwidth devices
需要有interrupt signals和interrupt service routine存在
不適合high-bandwidth devices
需要有DMA controller