Android资料

2010 - 02 - 27

smali反编译dex

文章分类:移动开发

  用Dedexer除了可以反编译其内附的dex,其余的都未成功过。smali可以顺利反编译,而且可以把你修改过的代码重新编译成classes.dex比较邪恶

 

反编译命令  baksmali-1.2.jar 【dex文件】 -o classes

重新编译  smali-1.2.jar 【需编译文件夹】

开源项目android4me 其AXMLPrinter2可以方便的把apk中已经序列化的xml还原为文本格式

 

 

把AXMLPrinter2.jar拷贝到C盘,在控制台cd到要解压出来的apk文件夹,执行下面的命令就可以把所有的xml还原文本格式了

 

for /r . %a in (*.xml) do @java -jar c:/AXMLPrinter2.jar "%a">>"%a".txt

 

 

  Notification通知界面可以用自己定义的界面来显示。下面是我实现的带进度条的通知效果

 

下面是主要实现部分代码,比较简单直接看代码可以了。

Java代码 复制代码
  1. nf =new Notification(R.drawable.icon,"带进度条的提醒",System.currentTimeMillis()) ;   
  2. nf.icon = R.drawable.icon;   
  3.        
  4. nf.contentView= new RemoteViews(this.getPackageName(),R.layout.notification);   
  5. nf.contentView.setProgressBar(R.id.ProgressBar01, 1000false);   
  6. nf.contentIntent=PendingIntent.getActivity( this0new Intent(this,remoteview.class) ,0);  
		nf =new Notification(R.drawable.icon,"带进度条的提醒",System.currentTimeMillis()) ;
		nf.icon = R.drawable.icon;
			
		nf.contentView= new RemoteViews(this.getPackageName(),R.layout.notification);
		nf.contentView.setProgressBar(R.id.ProgressBar01, 100, 0, false);
		nf.contentIntent=PendingIntent.getActivity( this, 0, new Intent(this,remoteview.class) ,0);

 

 

  BroadcastReceiver在android中是一特色功能.android系统的很多消息(如系统启动,新短信,来电话等)都通过BroadcastReceiver来分发.BroadcastReceiver的生命周期是短暂的,而且是消息一到达则创建执行完毕就立刻销毁的.为了说明这一点,我通过注册一个Alarm Service每7秒钟触发产生一次BroadcastReceive事件.

 

以下是BroadcastReceive的实现

Java代码 复制代码
  1. protected static final class AlarmReceiver extends BroadcastReceiver{   
  2.     public AlarmReceiver()   
  3.     {   
  4.         //查看类创建的进程id和线程id   
  5.         Log.i("AlarmReceiver.AlarmReceiver()", Calendar.getInstance().getTime().toLocaleString());   
  6.         Log.i("AlarmReceiver.AlarmReceiver() -> pid", String.valueOf(android.os.Process.myPid()));   
  7.         Log.i("AlarmReceiver.AlarmReceiver() -> tid", String.valueOf(android.os.Process.myTid()));   
  8.     }   
  9.     @Override  
  10.     public void onReceive(Context context, Intent intent) {   
  11.         // TODO Auto-generated method stub   
  12.         Log.i("AlarmReceiver.onReceive()", Calendar.getInstance().getTime().toLocaleString());   
  13.     }   
  14. }  
	protected static final class AlarmReceiver extends BroadcastReceiver{
		public AlarmReceiver()
		{
			//查看类创建的进程id和线程id
			Log.i("AlarmReceiver.AlarmReceiver()", Calendar.getInstance().getTime().toLocaleString());
			Log.i("AlarmReceiver.AlarmReceiver() -> pid", String.valueOf(android.os.Process.myPid()));
			Log.i("AlarmReceiver.AlarmReceiver() -> tid", String.valueOf(android.os.Process.myTid()));
		}
		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			Log.i("AlarmReceiver.onReceive()", Calendar.getInstance().getTime().toLocaleString());
		}
	}

 

通过一个"Start"按钮和一个"Stop"按钮来控制消息.

Java代码 复制代码
  1. public void onClick(View arg0) {   
  2.     // TODO Auto-generated method stub   
  3.     switch(arg0.getId()){   
  4.     case R.id.btnStart:   
  5.         Log.i("current pid", String.valueOf(android.os.Process.myPid()));   
  6.         Log.i("current tid", String.valueOf(android.os.Process.myTid()));   
  7.            
  8.         ((AlarmManager)this.getSystemService(Context.ALARM_SERVICE))   
  9.             .setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 7*1000,    
  10.                  PendingIntent.getBroadcast(this0new Intent(this,AlarmReceiver.class), 0)   
  11.                 );   
  12.         break;   
  13.     case R.id.btnStop:   
  14.         ((AlarmManager)this.getSystemService(Context.ALARM_SERVICE))   
  15.             .cancel(PendingIntent.getBroadcast(this0new Intent(this,AlarmReceiver.class), 0));   
  16.         break;   
  17.     }   
  18. }  
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
		switch(arg0.getId()){
		case R.id.btnStart:
			Log.i("current pid", String.valueOf(android.os.Process.myPid()));
			Log.i("current tid", String.valueOf(android.os.Process.myTid()));
			
			((AlarmManager)this.getSystemService(Context.ALARM_SERVICE))
				.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 7*1000, 
					 PendingIntent.getBroadcast(this, 0, new Intent(this,AlarmReceiver.class), 0)
					);
			break;
		case R.id.btnStop:
			((AlarmManager)this.getSystemService(Context.ALARM_SERVICE))
				.cancel(PendingIntent.getBroadcast(this, 0, new Intent(this,AlarmReceiver.class), 0));
			break;
		}
	}

 

打开logCat选择I,运行清理掉所有日志点击"Start"按钮

从上图可以看到AlarmReceiver类的实例是每触发一次就新建一次的,而且和程序是在同一个进程同一线程内.

 

IPC在android是一个非常独特的实现方式,它是通过binder 驱动来实现不同进程的通信的.

深入的了解清看李先静的<<Android IPC机制详解>>

 

 不同进程之间交换数据通过Parcelable包装交换数据.可以通过Intent在不同的进程之间传送数据.
 1,Parcelable包装交换数据实现
   a,新建一个实现Parcelable接口的Wrap类.示例

Java代码 复制代码
  1. public class Wrap implements Parcelable {   
  2.     public T Data;   
  3.     public int describeContents() {   
  4.         return 0;   
  5.     }   
  6.   
  7.     public void writeToParcel(Parcel dest, int flags) {   
  8.         dest.writeXX(Data);   
  9.     }   
  10.        
  11.     public Wrap(T value) {   
  12.          Data =value;   
  13.     }   
  14.   
  15.     private Wrap(Parcel in){   
  16.         Data=in.readXX();   
  17.     }   
  18.   
  19.     public static final Parcelable.Creator<Wrap> CREATOR = new Parcelable.Creator<Wrap>(){   
  20.         public Wrap createFromParcel(Parcel source) {   
  21.             return new Wrap(source);   
  22.         }   
  23.         public Wrap[] newArray(int size) {   
  24.             return new Wrap[size];   
  25.         }   
  26.      };   
  27. }//end class  
		public class Wrap implements Parcelable {
			public T Data;
			public int describeContents() {
				return 0;
			}

			public void writeToParcel(Parcel dest, int flags) {
				dest.writeXX(Data);
			}
			
			public Wrap(T value) {
				 Data =value;
			}

			private Wrap(Parcel in){
				Data=in.readXX();
			}

			public static final Parcelable.Creator<Wrap> CREATOR = new Parcelable.Creator<Wrap>(){
				public Wrap createFromParcel(Parcel source) {
					return new Wrap(source);
				}
				public Wrap[] newArray(int size) {
					return new Wrap[size];
				}
			 };
		}//end class
  注意:被包装的数据必须可序列化

   b,数据交互
     Bundle.putParcelable(String key,Parcel value) 传入数据
     Bundle.getParcelable(String key) 返回传入的Parcelable实例,再通过该实例获取数据

android数据的存储方式可以用标准的j2se的api文件存储和网络存储方式.以下是android的其他两种存储方式

1,Shared Preferences是一个简单键值对的xml格式的存储方式
     a,读取
        getPreferences (int mode) 返回SharedPreferences实例
 mode Activity.MODE_PRIVATE, Activity.MODE_WORLD_READABLE, Activity.MODE_WORLD_WRITEABLE
       
 getString (String key, String defValue) 获取数据
     b,写数据
       通过SharedPreferences(必须为MODE_PRIVATE或MODE_WORLD_WRITEABLE)实例的edit()返回Editor对象
       Editor.putString (String key, String value) 设置值
       Editor.commit() 提交保存
       Editor.clear()  清除所有数据
       Editor.remove (String key) 移除某一值

 

2,SQLite
    a,SQLiteDatabase类
     1),打开/关闭数据库
       openOrCreateDatabase(String name, int mode, CursorFactory factory)返回SQLiteDatabase实例 //数据库不存在则新建一个
       mode Context.MODE_PRIVATE, Context.MODE_WORLD_READABLE, Context.MODE_WORLD_WRITEABLE, Context.MODE_PRIVATE

       SQLiteDatabase类.close()关闭数据库
     2),执行数据库操作
       SQLiteDatabase.execSQL (String sql) //执行Sql语句
 
       long insert (String table, String nullColumnHack, ContentValues values)
       int update (String table, ContentValues values, String whereClause, String[] whereArgs)
       int delete (String table, String whereClause, String[] whereArgs)
       其中ContentValues是一个键值对,通过ContentValues.put方法设置列名和列值

       Cursor rawQuery (String sql, String[] selectionArgs) //执行的是语句
       Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
       Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
       Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,
              String orderBy, String limit)
       其中:
    columns 显示列
    selection 条件where部分
    selectionArgs ?替换符号.如selection为name =? and age=?那么selectionArgs可以为 new  String[]{"abc","20"}
      
       Cursor类
         int getCount() 返回记录数
  boolean isClosed () 判断关闭
  boolean isFirst ()
  boolean isLast ()

  boolean moveToFirst ()
  boolean moveToLast ()
  boolean moveToNext ()
  boolean moveToPosition (int position)
  boolean moveToPrevious ()

  boolean isNull (int columnIndex) 判断是否为空
  XXX getXXX(int columnIndex) 读取某一列的数据

     3),事务
       SQLiteDatabase.beginTransaction()  //打开
       SQLiteDatabase.setTransactionSuccessful() //提交
       SQLiteDatabase.endTransaction()   //结束

    b,SQLiteOpenHelper
      通过该继承该类可以获取到数据库创建/打开/升级等信息
      须实现的方法
      onCreate(SQLiteDatabase db)     //数据库被创建时触发
      onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)      //升级数据库

      其他重写
      onOpen(SQLiteDatabase db) 书库打开是触发

      方法
         SQLiteDatabase getReadableDatabase ()
  synchronized SQLiteDatabase getWritableDatabase ()

无论是Shared Preferences还是SQLite都是程序私有的其他程序无法直接访问.要想访问其他程序的数据须实现Content Provider.

Service拥有一个单独进程的模块.

 

 1,继承自Service类,须实现public IBinder onBind(Intent intent)


 2,通过startServie触发运行,stopService终止运行
   生命周期: onCreate(如果是第一次运行) -> onStart -> onDestroy


 3,绑定触发(调用4功能前提)
   boolean bindService (Intent service, ServiceConnection conn, int flags) //flags:Context.BIND_AUTO_CREATE
   ServiceConnection须实现两个方法
     onServiceConnected(ComponentName name, IBinder service)
     onServiceDisconnected(ComponentName name)
   相对应的方法unbindService (ServiceConnection conn)
   生命周期: onCreate(如果是第一次运行)-> onBind(仅一次,不可多次绑定)--> onUnbind-> onDestory

 

 4,怎样提供客户端调用Service方法
   a,新建aidl文件定义XX接口
   b,实现XX接口的XX.Stub()该类下实现XX接口定义的方法
   c,XX.Stub()实例返回给public IBinder onBind(Intent intent)
   d,在ServiceConnection.onServiceConnected(ComponentName name, IBinder service) 中通过
     XX.Stub.asInterface(service)返回XX接口实例


 5,获取系统正在运行的Services
   ActivityManager am = (ActivityManager)Activity.getSystemService(ACTIVITY_SERVICE);
   am.getRunningServices();

[传入]
 1,设置传入
  a,新建一个Bundle实例,Bundle.putXX添加数据,Intent.putExtras传递如Bundle参数
  b, Intent.putExtra设置键值对
 startActivityForResult启动另外一个Activity
 
[获取传入]
 getIntent().getExtras()返回Bundle存储其他Activity传入的数据

  b,传出返回值


 1,设置返回
 Intent.putExtra设置键值对
 Activity.setResult送出返回值

 

 2,获取返回值
 重写protected void onActivityResult(int requestCode, int resultCode, Intent data)
 data里存储的是返回值

用于接收外部事件(注意BroadcastReceiver是用时创建用完后即摧毁的)
 1,获取
   a,注册 Context.registerReceiver (BroadcastReceiver receiver, IntentFilter filter)
     receiver 为获取BroadcastReceiver事件的类
     filter   事件过滤器为sendBroadcast中的intent
     与之对应的Context.unregisterReceiver (BroadcastReceiver receiver)

     或在<application>节内添加
     <receiver android:name=".接收事件BoardCastReceiver类">
  <intent-filter android:name="要接受的事件">
  </intent-filter>
     </receiver>
   b,继承该类须实现 public void onReceive(Context context, Intent intent)方法

 2,产生
   Context.sendBroadcast
   如果传入的参数是Intent(name)那么name应该等于<intent-filter android:name="要接受的事件">

 

[Toast]
 1,显示提示信息
 public static Toast makeText (Context context, CharSequence text, int duration).show()
 public static Toast makeText (Context context, int resId, int duration).show()

 duration可以是:Toast.LENGTH_LONG,Toast.LENGTH_SHORT

 

[Notification]
 1,(NotificationManager)getSystemService(NOTIFICATION_SERVICE) 获取NotificationManager 通知管理器
 2,实例化Notification
  设置属性
    icon 图标
    tickerText 通知栏显示的内容
    defaults   震动(Notification.DEFAULT_VIBRATE) /声音(Notification.DEFAULT_SOUND) /光照(Notification.DEFAULT_LIGHTS) /Notification.DEFAULT_ALL
 3,通知被查看设置
   setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)
   contentIntent=PendingIntent.getActivity(this, 0,  new Intent(this, 该通知被点击时调用Activity.class), 0)
 4,显示
   NotificationManager.notify(int id, Notification notification)显示通知
 5,取消显示
   NotificationManager.cancel(int id)//id=notify中的id且大于0
   NotificationManager.cancelAll()取消全部通知
 6,其他
   振动加权限<uses-permission android:name="android.permission.VIBRATE"></uses-permission>

1,继承自TabActivity类

2,getTabHost()获取TabHost实例

3, LayoutInflater.from(this).inflate(界面布局Id, tabHost.getTabContentView(), true);

4,添加选项卡
    tabHost.addTab(
        tabHost.newTabSpec(选项卡Tag)   //返回TabSpec实例
 .setIndicator(选项卡显示标题) //或.setIndicator(CharSequence label, Drawable icon)
 .setContent(选项卡绑定视图Id)); //或.setContent(Intent intent) 设置其他Activity为选项卡

5,监听选项卡更改事件
    TabHost.setOnTabChangedListener 重写public void onTabChanged(String arg0)//arg0=选项卡Tag

6,方法
 setCurrentTab (int index)//设置第几个选项卡选中
 setCurrentTabByTag (String tag)设置选项卡选中,tag=选项卡Tag
      相对应的方法
 getCurrentTab ()
 getCurrentTabByTag ()

7,注意事项
    Toast在这种布局中不起作用

1,
  a,创建.重写public boolean onCreateOptionsMenu(Menu menu)
    menu.add添加菜单项
  b,监听菜单选中事件.重写public boolean onOptionsItemSelected(MenuItem item)//item.getItemId()为菜单项的id

 

2,带子菜单的菜单
 重写public boolean onCreateOptionsMenu(Menu menu)
 menu.addSubMenu(CharSequence title)返回父菜单SubMenu,SubMenu.add添加其下级菜单

 

3,从XML加载菜单
 getMenuInflater()返回MenuInflater实例或 new MenuInflater(Context context)
 MenuInflater.inflate(int menuRes, Menu menu) 加载XML
 
4,上下文菜单
 1,registerForContextMenu(View v)注册要显示上下文菜单的控件
 2,重写public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) 创建上下文菜单

1,创建
 a,new AlertDialog.Builder(Context  context)
 b,一些设置
 c,create()创建
 d,show()显示
 e,dismiss()退出对话框

 

2,常用方法
 setIcon:设置图标
 setTitle:设置标题
 setPositiveButton:设置 确定按钮
 setNegativeButton:设置 取消按钮
 setNeutralButton:设置 忽略按钮
 setCancelable(Boolean arg0)//按回退键是否可以取消

 

3,可选但唯一的方法
 setMessage 设置显示消息
 setItems   显示单选列表,选择后对话框退出
 setSingleChoiceItems 显示单选列表,选择后对话框不退出
 setMultiChoiceItems  显示多选列表,选择后对话框不退出
   
4,从资源文件加载对框视图
 通过setView( LayoutInflater.from(Context context).inflate(resource, null) )安装视图

 

5,通过Activity.showDialog(int id)来显示对话框
 重写Activity的以下方法
 protected Dialog onCreateDialog(int id) //Dialog第一次创建时调用.这里需要通过AlertDialog.create()返回创建的对话框
 protected onPrepareDialog(int id, Dialog dialog)  //Dialog显示前调用

 

6,其他对话框
     a,ProgressDialog对话框
 setIndeterminate 设置进度条是否自动运转
 setProgressStyle 设置显示风格.ProgressDialog.STYLE_HORIZONTAL/ProgressDialog.STYLE_SPINNER
 setProgress  设置进度,只有对话框显示后才有用
 setCancelable(Boolean arg0)//按回退键是否可以取消,进度条对框框默认不取消
 setMessage() 设置显示内容
 最简单的显示方法: public static ProgressDialog show (Context context, CharSequence title, CharSequence message)
     b,TimePickerDialog/DatePickerDialog

个人学习android做的笔记,贴出来备忘.

 

1,EditText
     主要函数:setText/getText设置/获取文本内容,setHint设置缺省显示内容;

 

2,RadioGroup,RadioButton
 RadioButton的isChecked()判断是否被选中
 获取选中RadioButon的ID:设置RadioGroup.setOnCheckedChangeListener方法
 public onCheckedChanged(RadioGroup group,int checkedId)//checkedId是选中RadioButton的ID

 

3,CheckBox
 isChecked()判断是否被选中
 setOnCheckedChangeListener方法监视选中状态改变情况

 

4,Spinner
    a,显示数据
 1),硬编码加载
  通过setAdapter方法设置类型为ArrayAdapter (Context context, int textViewResId, String []objects)
   textViewResourceId:显示内容的ViewID默认设置为R.layout.simple_spinner_item
   objects:显示的内容
 2),从资源文件中加载
  ArrayAdapter.createFromResource (Context context, int textArrayResId, int textViewResId) //textArrayResId是资源ID
  返回ArrayAdapter<CharSequence>
    b,设置下拉列表的风格(可选)
 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    c,监听选项选择变更事件
 setOnItemSelectedListener设置监听去
 Spinner.OnItemSelectedListener类须实现以下两个方法

 public void onItemSelected (AdapterView<?> parent, View view, int position, long id)//view 选中项实例,position选择项在adapter中的位置
 public void onNothingSelected(AdapterView<?> arg0)
    d,设置选中项
 Spinner.setSelection(position)//索引从0开始

    d,获取选中项
 getSelectedItemPosition ()
 getSelectedItem () //该值toString()则为选中内容的字符串
 getSelectedItemId ()
 getSelectedView ()

 

5,AutoCompleteTextView
 1,setAdapter设置数据adapter
 2,设置输入多少个字符显示提示AutoCompleteTextView.setThreshold(int threshold)

  6,MultiAutoCompleteTextView (允许输入多值,多值之间会自动地用指定的分隔符分开)
 1,setAdapter设置数据adapter
 2,setTokenizer设置提示Tokenizer缺省的为new MultiAutoCompleteTextView.CommaTokenizer()以逗号分隔

 

7,DatePicker,TimePicker
 a,DatePicker
 1),设置初始显示日期init(int Year, int month, int day, new DatePicker.OnDateChangedListener(){
   public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth){}
   })
 2),获取设置值 getYeah()/getMonth()/getDayOfMonth()
 b,TimePicker
 1),设置setCurrentHour (Integer currentHour) /setCurrentMinuter (Integer currentHour)
 2,setIs24HourView(Boolean)设置是否为24小时制显示
 3,监听设置改变setOnTimeChangedListener

 c,获取系统当期时间和日期
 Calendar.getInstance()返回Calendar
 Calendar.get (int field) ---field 可以为Calendar.YEAR/ Calendar.MONTH/ Calendar.DAY_OF_MONTH/ Calendar.HOUR_OF_DAY/ Calendar.MINUTE

 

 8,ImageView/ImageButton
 主要方法
  setImageResource 设置显示图片
  setAlpha 设置Alpha
  invalidate 强制更新
  setScaleType( ScaleType st) 设置图片显示方式,st是一枚举
  setAdjustViewBounds 设置是否调整控件适应图片大小
  setBackgroundResource 设置背景

  

9,ImageSwitcher(显示一系列的图片,但当前只显示一张图片)
     显示数据
 setFactory( ViewSwitcher.ViewFactory  factory)设置要显示的数据
 ,ViewFactory接口须实现方法public View makeView(){}负责提供当前显示的视图(ImageView),且View必须为新实例
     方法
 setImageResource设置当前显示的图片
 getCurrentView()返回当前显示的视图
 setInAnimation(Animation ani)设置视图装载入时的动画效果,AnimationUtils.loadAnimation(Context context, int id) 获取动画效果android.R.anim.XX
 setOutAnimation(Animation ani)设置视图装载入时的动画效果

  

 

10,Gallery(显示一系列的图片,提供拖动等特效)
     显示数据
 setAdapter(SpinnerAdapter adapter)设置数据适配器.
   数据适配器可以继承自BaseAdapter,该类 public View getView(int position, View convertView, ViewGroup parent) 返回当前选择的视图(ImageView)
     选项选中监听setOnItemSelectedListener


  

11,GridView(表格显示一系列图片)
     显示数据
 setAdapter(ListAdapter adapter)设置数据适配器.
   数据适配器可以继承自BaseAdapter,该类 public View getView(int position, View convertView, ViewGroup parent) 返回当前选择的视图(ImageView)
     选项选中监听setOnItemSelectedListener
     getSelectedView()返回当前选中的视图

  

12,ScrollView

  

13,ProgressBar
 setIndeterminate 设置进度条是否自动运转
 setProgressStyle 设置显示风格.ProgressDialog.STYLE_HORIZONTAL/ProgressDialog.STYLE_SPINNER
 setProgress  设置进度
 setMax 设置最大值
 getProgress()获取当前进度

   

14,SeekBar
 方法
 setMax 设置最大值
 getProgress()获取当前值

 setProgress  设置值
 setIndeterminate

 监听器
 setOnSeekBarChangeListener其下有三个方法
  public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) //数值变更,arg1新值,是否Touch
  public void onStartTrackingTouch(SeekBar seekBar)//开始拖动
  public void onStopTrackingTouch(SeekBar seekBar) //结束拖动

   

 

15,ListView
      a,显示数据setAdapter(ListAdapter adapter)
       adapter可为new SimpleCursorAdapter/SimpleAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
       layout 用来显示数据的模板.显示一列可用android.R.layout.simple_list_item_1 两列可用android.R.layout.simple_list_item_2
               多列则需要自己实现xml视图布局文件
       c  数据(可用ArrayList构造数据)
       from ':
       to 用来显示对应列的空件id
      b,动态增删数据
        adapter.notifyDataSetChanged()
      d,设置背景色
        setBackGroudRource

2010 - 02 - 25

Android的布局

文章分类:移动开发

   UI设计中使用XML(类XML)语言描述是一大趋势.AS中的界面UI部分是类XML,微软的WPF和Silverlight也采用类XML语言,当然在网页上的XHTML更是XML大应用了.或许由于Google是互联起家的缘故,所以在Android的界面布局中到处可以看到网页布局的影子.当你看到findViewById是否会想去js中经常用到的document.getElementById呢?当你用LinearLayout/RelativeLayout/TableLayou布局是否会想起网页中DIV+CSS和经典的Table布局呢?
下面简单介绍Android的几个重要布局

1,LinearLayout
   LinearLayout是一种流水线型的布局方式.它可以通过orientation属性设置线性排列的方向是垂直(vertical)还是纵向(horizontal).
   两种布局示意图


2,RelativeLayout
RelativeLayout(相对布局)是一种非常灵活的布局方式,几乎没有见过可能是Android独创的.这个布局的一个要点是里面的控件可以指定其上下左右对齐的控件.所以在要设置诸如:layout_alignBottom,layout_toRightOf等等属性.
   一些常用到属性的描述:
   layout_toRightOf / layout_toLeftOf --紧凑右边/左边控件.属性值指定控件id
   layout_alignTop /layout_alignBottom / layout_alignRight/ layout_alignLeft --顶端/底部/右边/左边对齐的控件
    layout_above --紧凑其上的控件.属性值指定控件id

   layout_alignParentTop/ layout_alignParentBottom --是否贴近父容器的顶部/底部
   layout_alignParentRight/layout_alignParentLeft --是否贴近父容器的右边/左边  

   RelativeLayout布局示意图下  
   


3,AbsoluteLayout
   绝对布局是UI布局中应用的最广的布局方式,就是指定控件的X/Y坐标使控件固定在一个点上.Windows开发的程序员几乎都是这种布局方式.不过这种布局在手机开发中有很大的弊病,因为不同的手机类型的屏幕尺寸可能是不尽相同的,这也是j2me在做不同手机开发时非常头痛的地方.
   Andorid的控件描述X/Y坐标的属性是:layout_x,layout_y.
   X/Y坐标的计量单位在Android有以下几种:
   px:像素
   dip:依赖设备的像素.这个是android独创.
   sp:比例像素
   pt:点
   in:英尺
   mm:毫米

   AbsoluteLayout布局示意图  
    

4,TableLayout
   Table布局在网页设计中是一种非常常见的布局方式.不过Android的TableLayout与网页中的table布局并不相同.以下是TableLayout布局中几个重要的属性和对象
    collpaseColumns --隐藏TableLayout里TableRow的列X
    stretchColumns -- 设置列X为可伸展列
    shrinkColumns -- 设置列X为可收缩列
    TableRow -- 行对象

    TableLayout示意图
     

2010 - 02 - 25

Dalvik虚拟机

文章分类:移动开发

     Google于2007年底正式发布了Android SDK, 作为 Android系统的重要特性,Dalvik虚拟机也第一次进入了人们的视野。它对内存的高效使用,和在低速CPU上表现出的高性能,确实令人刮目相看。依赖于底层Posix兼容的操作系统,它可以简单的完成进程隔离和线程管理。每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。

      很多人认为Dalvik虚拟机是一个Java虚拟机,因为Android的编程语言恰恰就是Java语言。但是这种说法并不准确,因为Dalvik虚拟机并不是按照Java虚拟机的规范来实现的,两者并不兼容;同时还要两个明显的不同:

  • Java虚拟机运行的是Java字节码,而Dalvik虚拟机运行的则是其专有的文件格式DEX(Dalvik Executable)。
  • 在Java SE程序中的Java类会被编译成一个或者多个字节码文件(.class)然后打包到JAR文件,而后Java虚拟机会从相应的CLASS文件和JAR文件中获取相应的字节码;Android应用虽然也是使用Java语言进行编程,但是在编译成CLASS文件后,还会通过一个工具(dx)将应用所有的CLASS文件转换成一个DEX文件,而后Dalvik虚拟机会从其中读取指令和数据。

Dalvik和Android系统

      Android作为新一代的基于Linux的开源手机操作系统,其系统架构由下而上可以分为以下几部分:

  1. Linux内核
  2. 本地库
  3. Android运行库
  4. 应用框架
  5. 应用
    http://dl.javaeye.com/upload/picture/pic/55611/99ce905e-6e53-32eb-a4b4-39975324e610.jpg

 

      如图所示,Android运行库包括两部分:核心库和Dalvik虚拟机。核心库包括了最基本的类库,如data structure, network, Utilities, File system等的,很多实现代码都是来自Apache Harmony项目,主要目的是保证虚拟机的类库能够和Java SE的类库最大可能的兼容,从而降低应用开发者从Java SE阵营转移到Android开发阵营的难度,增加其可用性。Dalvik虚拟机主要是完成对象生命周期的管理,堆栈的管理,线程管理,安全和异常的管理,以及垃圾回收等等重要功能。

Dalvik虚拟机的主要特征

      Dalvik虚拟机非常适合在移动终端上使用,相对于在桌面系统和服务器系统运行的虚拟机而言,它不需要很快的CPU速度和大量的内存空间。根据Google的测算,64M的RAM已经能够令系统正常运转了。其中24M被用于底层系统的初始化和启动,另外20M被用于高层启动高层服务。当然,随着系统服务的增多和应用功能的扩展,其所消耗的内存也势必越来越大。

      归纳起来,Dalvik虚拟机有如下几个主要特征:

  • 专有的DEX文件格式

      DEX是Dalvik虚拟机专用的文件格式,而问什么弃用已有的字节码文件(CLASS文件)而采用新的格式呢?

    1.一个应用中会定义很多类,编译完成后即会有很多相应的CLASS文件,CLASS文件间会有不少冗余的信息;而DEX文件格式会把所有的CLASS文件内容整合到一个文件中。这样,除了减少整体的文件尺寸,I/O操作,也提高了类的查找速度。

      原来每个类文件中的常量池,在DEX文件中由一个常量池来管理,具体方式如下图:
       
       
       

 

      2.增加了新的操作码的支持

        3.文件结构尽量简洁,使用等长的指令,借以提高解析速度

      4. 尽量扩大只读结构的大小,借以提高跨进程的数据共享

      如何生成DEX文件呢?Android系统和Dalvik虚拟机提供了工具(DX),在把Java源代码编译成CLASS文件后,使用DX工具。

 

  • DEX的优化

      DEX文件的结构是紧凑的,然是如果我们还想要求运行时的性能有进一步提高,我们就仍然需要对DEX文件进行进一步优化。优化主要是针对以下几个方面:

  1. 调整所有字段的字节序(LITTLE_ENDIAN)和对齐结构中的没一个域
  2. 验证DEX文件中的所有类
  3. 对一些特定的类进行优化,对方法里的操作码进行优化

      优化后的文件大小会有所增加,应该是原DEX文件的1-4倍。

      优化发生的时机有两个:对于预置应用,可以在系统编译后,生成优化文件,以ODEX结尾。这样在发布时除APK文件(不包含DEX)以外,还有一个相应的ODEX文件;对于非预置应用,包含在APK文件里的DEX文件会在运行时被优化,优化后的文件将被保存在缓存中。

  • 基于寄存器

      相对于基于堆栈的虚拟机实现,基于寄存器的虚拟机实现虽然在硬件通用性上要差一些,但是它在代码的执行效率上却更胜一筹。一般来讲,虚拟机中指令的解释执行时间主要花在以下三个方面:

  1. 分发指令
  2. 访问运算数
  3. 执行运算

      其中“分发指令”这个环节对性能的影响最大。在基于寄存器的虚拟机里,可以更为有效的减少冗余指令的分发和减少内存的读写访问,如:

 


      虽然Dalvik虚拟机并没有使用目前流行的虚拟机技术,如JIT,但是根据Google的报告,这个功能的缺失并没有另Dalvik虚拟机在性能上有所损失。我们也同时相信,Dalvik虚拟机的性能还有进一步提高的空间。

  • 一个应用,一个虚拟机实例,一个进程

      每一个Android应用都运行在一个Dalvik虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制,内存分配和管理,Mutex等等都是依赖底层操作系统而实现的。所有Android应用的线程都对应一个Linux线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制。

      不同的应用在不同的进程空间里运行,加之对不同来源的应用都使用不同的Linux用户来运行,可以最大程度的保护应用的安全和独立运行。

      Zygote是一个虚拟机进程,同时也是一个虚拟机实例的孵化器,每当系统要求执行一个Android应用程序,Zygote就会FORK出一个子进程来执行该应用程序。这样做的好处显而易见:Zygote进程是在系统启动时产生的,它会完成虚拟机的初始化,库的加载,预置类库的加载和初始化等等操作,而在系统需要一个新的虚拟机实例时,Zygote通过复制自身,最快速的提供个系统。另外,对于一些只读的系统库,所有虚拟机实例都和Zygote共享一块内存区域,大大节省了内存开销。


      应用程序包(APK)被发布到手机上后,运行前会对其中的DEX文件进行优化,优化后的文件被保存到缓存区域(优化后的格式被称为DEY),虚拟机会直接执行该文件。如果应用包文件不发生变化,DEY文件不会被重新生成。

 

Android应用开发和Dalvik虚拟机

    Android应用所使用的编程语言是Java语言,和Java SE一样,编译时使用Sun JDK将Java源程序编程成标准的Java字节码文件(.class文件),而后通过工具软件DX把所有的字节码文件转成DEX文件(classes.dex)。最后使用Android打包工具(aapt)将DEX文件,资源文件以及AndroidManifest.xml文件(二进制格式)组合成一个应用程序包(APK)。应用程序包可以被发布到手机上运行。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值