`
George_ghc
  • 浏览: 94867 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
条码扫描二维码扫描——ZXing android 源码简化
http://www.cnblogs.com/keyindex/archive/2011/06/08/2074900.html
前言

  最近公司的Android项目需要用到摄像头做条码或二维码的扫描,Google一下,发现一个以Apache License 2.0 开源的 ZXing项目。Zxing项目里的Android实现太过复杂多余东西太多,得对其进行简化。
前提条件

  下载源代码:点击这里

  编译核心库:Zxing的主页上有介绍具体步骤,大家也可以参照这篇博文:android 条码识别软件开发全解析(续2详解绝杀!)
导入项目

  打开Eclipse 导入 源码中的 Android 项目,然后右击项目 选择“Build path”——》"Add External Archives" 把核心库 core.jar文件加入到项目中。

此时编译一下项目,会发现报错,“ Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?”之类的。打开raw 下的Values 发现错误是在一个<String>上。这里把 “preferences_custom_product_search_summary” 里的  %s  %f  全部都改成  %1$s  %1$f(因为我们用不到多国语言,建议只保留默认的Value ,其他全部删除)。

  原因:由于新的SDK采用了新版本的aapt(Android项目编译器),这个版本的aapt编译起来会比老版本更加的严格,然后在Android最新的开发文档的描述String的部分,已经说明如何去设置 %s 等符号

  “If you need to format your strings using String.format(String, Object...) , then you can do so by putting your format arguments in the string resource. For example, with the following resource:

  <string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>

  In this example, the format string has two arguments: %1$s is a string and %2$d is a decimal number. You can format the string with arguements from your application...“

  经过以上步骤后项目应该就可以运行了。

  但是ZXing的android项目东西太多了,有很多是我们不需要的,得新建另一个项目简化它。
简化

  在开始前大致介绍一下简化ZXing需要用到各个包 、类的职责。

    * CaptureActivity。这个是启动Activity 也就是扫描器(如果是第一安装,它还会跳转到帮助界面)。
    * CaptureActivityHandler 解码处理类,负责调用另外的线程进行解码。
    * DecodeThread 解码的线程。
    * com.google.zxing.client.android.camera 包,摄像头控制包。
    * ViewfinderView 自定义的View,就是我们看见的拍摄时中间的框框了。

新建另一个项目

  新建另一个项目将启动的Activity命名为CaptureActivity,并导入核心库。项目新建完成后我们打开 CaptureActivity 的布局文件,我这里为main。把里面的XML修改为:
复制代码
1 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:layout_width="fill_parent" android:layout_height="fill_parent">
3 <SurfaceView android:id="@+id/preview_view"
4 android:layout_width="fill_parent" android:layout_height="fill_parent"
5 android:layout_centerInParent="true"/>
6
7 <com.Zxing.Demo.view.ViewfinderView
8 android:id="@+id/viewfinder_view" android:layout_width="fill_parent"
9 android:layout_height="fill_parent" android:background="@android:color/transparent"/>
10 <TextView android:layout_width="wrap_content"
11 android:id="@+id/txtResult"
12 android:layout_height="wrap_content" android:text="@string/hello"/>
13
14  </FrameLayout>
复制代码

  可以看到在XML里面用到了 ViewfinderView 自定义view 。所以新建一个View 的包,然后把:ViewfinderView 和 ViewfinderResultPointCallback 靠到里面(记得对应修改XML里面的包)。

打开 CaptureActivity 覆盖 onCreate 方法:
复制代码
1 @Override
2 publicvoid onCreate(Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4 setContentView(R.layout.main);
5 //初始化 CameraManager
6   CameraManager.init(getApplication());
7
8 viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
9 txtResult = (TextView) findViewById(R.id.txtResult);
10 hasSurface =false;
11 inactivityTimer =new InactivityTimer(this);
12 }
复制代码

  这里调用到的 CameraManager 类是控制摄像头的包里的类。新建一个camera包把:com.google.zxing.client.android.camera 里面的类全部拷入,另外我把PlanarYUVLuminanceSource也拷入到这个包里面。根据错误的提示来修正代码,主要是修改正包结构。(整个简化的流程都是如此:“根据错误提示,修改代码”)。

  在修改的过程中,有很多是关于R 资源的问题,在此我们需要将Values  里面的两个xml资源文件拷入项目中:colos.xml 和ids.xml 。 ctrl+b 一下看看error 是不是少了很多。在CameraManager中有些地方需要用到项目的配置,这里需要把配置直接写入代码中:
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
//是否使用前灯
// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
// FlashlightManager.enableFlashlight();
// }
FlashlightManager.enableFlashlight();

   使用摄像头需要加入相应的权限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>

  当View 和 camera 包里的错误修正完成后,我们继续来看CaptureActivity。

覆盖onResume方法初始化摄像头:
复制代码
@Override
protectedvoid onResume() {
super.onResume();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
decodeFormats =null;
characterSet =null;

playBeep =true;
AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
playBeep =false;
}
initBeepSound();
vibrate =true;
}
复制代码
initCamera
复制代码
1 privatevoid initCamera(SurfaceHolder surfaceHolder) {
2 try {
3 CameraManager.get().openDriver(surfaceHolder);
4 } catch (IOException ioe) {
5 return;
6 } catch (RuntimeException e) {
7 return;
8 }
9 if (handler ==null) {
10 handler =new CaptureActivityHandler(this, decodeFormats,
11 characterSet);
12 }
13 }
复制代码
SurfaceHolder接口实现
复制代码
@Override
publicvoid surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {

}

@Override
publicvoid surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface =true;
initCamera(holder);
}

}

@Override
publicvoid surfaceDestroyed(SurfaceHolder holder) {
hasSurface =false;

}
复制代码

initCamera () 方法用于初始化摄像头,如果排除了所有的error ,运行项目时就可以看到大致扫描界面了。 surfaceHolder.addCallback(this);表示让CaptureActivity实现其callback接口。

handler = new CaptureActivityHandler(this, decodeFormats,characterSet) 用于进行扫描解码处理。
解码

  上面的步骤主要都是用于对摄像头的控制,而解码的真正工作入口是在CaptureActivityHandler 里面的。新建一个Decoding包把以下文件拷入包中:

    * CaptureActivityHandler
    * DecodeFormatManager
    * DecodeHandler
    * DecodeThread
    * FinishListener
    * InactivityTimer
    * Intents

由于我们的包结构和Zxing 项目的有所不同所以需要注意一下类的可访问性

同样开始ctrl+B 编译一下,然后开始修正错误。

  在CaptureActivityHandler 里 把 handleMessage 里的部分方法先注释掉如:“decode_succeeded ”分支,这是解码成功时调用 CaptureActivity 展示解码的结果。

在DecodeThread 类里,修改部分涉及Preference配置的代码:
复制代码
DecodeThread(CaptureActivity activity,
Vector<BarcodeFormat> decodeFormats,
String characterSet,
ResultPointCallback resultPointCallback) {

this.activity = activity;
handlerInitLatch =new CountDownLatch(1);

hints =new Hashtable<DecodeHintType, Object>(3);

//// The prefs can't change while the thread is running, so pick them up once here.
// if (decodeFormats == null || decodeFormats.isEmpty()) {
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
// decodeFormats = new Vector<BarcodeFormat>();
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D, true)) {
// decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
// }
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true)) {
// decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
// }
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, true)) {
// decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
// }
// }
if (decodeFormats ==null|| decodeFormats.isEmpty()) {
decodeFormats =new Vector<BarcodeFormat>();
decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);

}

hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);

if (characterSet !=null) {
hints.put(DecodeHintType.CHARACTER_SET, characterSet);
}

hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
}
复制代码

这里是设置 解码的类型,我们现在默认将所有类型都加入。

错误类型基本上都是:包结构、PreferencesActivity 的配置 、类可访问性的问题。根据错误提示耐心把错误解决。
返回解码结果

   还记得在 CaptureActivityHandler 的 messagehandler 里注销掉的Case分支吗?现在CaptureActivity 里实现它。
复制代码
publicvoid handleDecode(Result obj, Bitmap barcode) {
inactivityTimer.onActivity();
viewfinderView.drawResultBitmap(barcode);
playBeepSoundAndVibrate();
txtResult.setText(obj.getBarcodeFormat().toString() +":"
+ obj.getText());
}
复制代码
最后

  ZXing的简化已基本完成,有几位是可以运行成功的?呵呵。

下面是CaptureActivity的源码:
CaputreActivity
复制代码
publicclass CaptureActivity extends Activity implements Callback {

private CaptureActivityHandler handler;
private ViewfinderView viewfinderView;
privateboolean hasSurface;
private Vector<BarcodeFormat> decodeFormats;
private String characterSet;
private TextView txtResult;
private InactivityTimer inactivityTimer;
private MediaPlayer mediaPlayer;
privateboolean playBeep;
privatestaticfinalfloat BEEP_VOLUME =0.10f;
privateboolean vibrate;

/** Called when the activity is first created. */
@Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//初始化 CameraManager
CameraManager.init(getApplication());

viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
txtResult = (TextView) findViewById(R.id.txtResult);
hasSurface =false;
inactivityTimer =new InactivityTimer(this);
}

@Override
protectedvoid onResume() {
super.onResume();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
decodeFormats =null;
characterSet =null;

playBeep =true;
AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
playBeep =false;
}
initBeepSound();
vibrate =true;
}

@Override
protectedvoid onPause() {
super.onPause();
if (handler !=null) {
handler.quitSynchronously();
handler =null;
}
CameraManager.get().closeDriver();
}

@Override
protectedvoid onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}

privatevoid initCamera(SurfaceHolder surfaceHolder) {
try {
CameraManager.get().openDriver(surfaceHolder);
} catch (IOException ioe) {
return;
} catch (RuntimeException e) {
return;
}
if (handler ==null) {
handler =new CaptureActivityHandler(this, decodeFormats,
characterSet);
}
}

@Override
publicvoid surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {

}

@Override
publicvoid surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface =true;
initCamera(holder);
}

}

@Override
publicvoid surfaceDestroyed(SurfaceHolder holder) {
hasSurface =false;

}

public ViewfinderView getViewfinderView() {
return viewfinderView;
}

public Handler getHandler() {
return handler;
}

publicvoid drawViewfinder() {
viewfinderView.drawViewfinder();

}

publicvoid handleDecode(Result obj, Bitmap barcode) {
inactivityTimer.onActivity();
viewfinderView.drawResultBitmap(barcode);
playBeepSoundAndVibrate();
txtResult.setText(obj.getBarcodeFormat().toString() +":"
+ obj.getText());
}

privatevoid initBeepSound() {
if (playBeep && mediaPlayer ==null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it
// too loud,
// so we now play on the music stream.
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer =new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(beepListener);

AssetFileDescriptor file = getResources().openRawResourceFd(
R.raw.beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(),
file.getStartOffset(), file.getLength());
file.close();
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
} catch (IOException e) {
mediaPlayer =null;
}
}
}

privatestaticfinallong VIBRATE_DURATION =200L;

privatevoid playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer !=null) {
mediaPlayer.start();
}
if (vibrate) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}

/**
* When the beep has finished playing, rewind to queue up another one.
*/
privatefinal OnCompletionListener beepListener =new OnCompletionListener() {
publicvoid onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo(0);
}
};
复制代码

简化过的包结构图:

 

 简化后的ZXing 更加方便我们了解ZXing项目 是如何解码的。只要仔细查看源码,进行单点跟踪调试,相信大家很容易能理解。
顾客是上帝

   很多人留言要源码, 其实我这不是什么源码,我只是把ZXing的东西简化了一下而已。事实上我也不喜欢直接放源码项目,这样大家就不想读ZXing的源码了。

下面是我简化的版本:Zxing简化

很多人需要Core 核心包(其实ZXing的源码里面就有),这里提供下我写文章时的版本 1.6的:

Zxing
android:installLocation简析
在Froyo(android 2.2,API Level:8)中引入了android:installLocation.通过设置该属性可以使得开发者以及用户决定程序的安装位置.

android:installLocation隶属于AndroidManifest.XML中的manifest节点.如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="string"
          android:sharedUserId="string"
          android:sharedUserLabel="string resource" 
          android:versionCode="integer"
          android:versionName="string"
          android:installLocation=["auto" | "internalOnly" | "preferExternal"] >
    . . .
</manifest>

android:installLocation可以设置为"auto"、"internalOnly"、"preferExternal"三个值中的任何一个.

  auto:程序可能被安装在外部存储介质上(例如:SD Card),但是默认会被安装到手机内存中.当手机内存为空时,程序将被安装到外部存储介质上.当程序安装到手机上后,用户      可以决定把程序放在外部储介质还是内存中.

  internalOnly:默认值.当设置为该值时,程序只能被安装在内存中,如果内存为空,则程序将不能成功安装.

  preferExternal:将程序安装在外部存储介质上,但是系统不保证程序一定会被安装到外部存储介质上.当外部存储介质不可以或空时,程序将被安装到内存中.程序使用了for      ward-locking机制时也将被安装到内存中,因为外部存储不支持此机制.程序安装后,用户可以自由切换程序应该在外部还是内部存储介质上.

注意:当程序使用了Google Play的Copy Protection特性时,只能安装到内存中.

当程序被安装到外部存储介质时,

① .apk文件将被移动到外部存储介质上,但是程序的数据仍然会在内存中

② 保存.apk文件的容器将会使用一个随机生成的密钥进行加密,这样只有安装该程序的设置可以使用存在外部存储介质上的数据.

警告:当外部存储介质被卸载时,安装在该外部存储介质上的程序将立刻被终止掉!

向后兼容性:

  声明了android:installLocation,但android:minSdkVersion小于8时,我们使用不低于Froyo的AVD进行编译,这样在低于Froyo的系统中android:installLocation将被忽略,而不低于Fro  yo的系统中将使用我们指定的android:installLocation.

当我们的程序具有如下行为时我们不应该将程序安装到外部存储介质上

  ①Service

    正在运行的服务将被终止,当外部存储介质被重新加载时服务不会被重启.

  ②Alarm Service

    闹钟服务将被取消,开发者必须在外部存储介质重新加载后重新注册闹钟服务.

  ③Input Method Engines

    输入法将被换成系统输入法,当外部存储介质被重新加载后用户可以通过系统设置来启动我们的输入法

  ④Live Wallpapers

    我们的动态壁纸将被替换为默认的动态壁纸.外部存储介质重载后,用户可以更换回来.

  ⑤Live Folders

    我们的动态文件夹将被移出.

  ⑥App Widgets

    我们的小部件将被移出,通常只有系统重启后我们的小部件才可用.

  ⑦Account Managers

    使用AccountManager创建的账户将会消失,直至存储介质被重新加载.

  ⑧Sync Adapters

    只有外部存储介质被重新加载时,我们的同步功能才可用

  ⑨Device Administrators

    我们的DeviceAdminReceiver将会失效

  ⑩监听开机结束事件

    系统会在加载外部存储介质之前发送ACTION_BOOT_COMPLETED广播.因此安装在外部存储介质的程序将不能接受开机广播.

通常,只要我们没有使用上述的特性,我们就可以将我们的程序安装到外部存储介质上.例如,大的游戏程序.当我们的APK文件有几M大时我们应该认真的考虑是否要将程序移动到外部存储介质上以帮助用户节省内存.
Jakarta-Common-Codec使用笔记
commons codec 提供 base64, hex, 及 metaphone, soundex 等编码演算。

下载地址:http://commons.apache.org/codec/

A.Base64 编解码

package demo;

import org.apache.commons.codec.binary.Base64;

public class Base64Test {

    public static void main(String[] args) {

        Base64 base64 = new Base64();
        String str = "中文";
        byte[] enbytes = null;
        String encodeStr = null;
        byte[] debytes = null;
        String decodeStr = null;

        enbytes = base64.encode(str.getBytes());
        encodeStr = new String(enbytes);
        debytes = base64.decode(enbytes);
        decodeStr = new String(debytes);
    
        System.out.println("编码前:" + str);
        System.out.println("编码后:" + encodeStr);
        System.out.println("解码后:" + decodeStr);
    }
}

B.Hex 编解码

 

 

package demo;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class HexTest {

    public static void main(String[] args) throws DecoderException {

        Hex hex = new Hex();
        String str = "中文";
        char[] enbytes = null;
        String encodeStr = null;
        byte[] debytes = null;
        String decodeStr = null;

        enbytes = hex.encodeHex(str.getBytes());
        encodeStr = new String(enbytes);
        debytes = hex.decodeHex(enbytes);
        decodeStr = new String(debytes);

        System.out.println("编码前:" + str);
        System.out.println("编码后:" + encodeStr);
        System.out.println("解码后:" + decodeStr);
    }
}

C.Metaphone 及 Soundex 编码

 

package demo;

import org.apache.commons.codec.language.Metaphone;
import org.apache.commons.codec.language.RefinedSoundex;
import org.apache.commons.codec.language.Soundex;

public class LanguageTest {

    public static void main(String[] args) {

        Metaphone metaphone = new Metaphone();
        RefinedSoundex refinedSoundex = new RefinedSoundex();
        Soundex soundex = new Soundex();
        for (int i = 0; i < 2; i++) {
            String str = (i == 0) ? "resume" : "resin";
            String mString = null;
            String rString = null;
            String sString = null;
            try {
                mString = metaphone.encode(str);
                rString = refinedSoundex.encode(str);
                sString = soundex.encode(str);
            } catch (Exception ex) {
                ;
            }
            System.out.println("Original:" + str);
            System.out.println("Metaphone:" + mString);
            System.out.println("RefinedSoundex:" + rString);
            System.out.println("Soundex:" + sString + "\n");
        }
    }
}

Metaphone 建立出相同的key给发音相似的单字, 比 Soundex 还要准确, 但是 Metaphone 没有固定长度, Soundex 则是固定第一个英文字加上3个数字. 这通常是用在类似音比对, 也可以用在 MP3 的软件开发.
MediaMetadataRetriever 使用
MediaMetadataRetriever retriever = new MediaMetadataRetriever();//该类的替代类是哪个啊?
        try {
            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
            retriever.setDataSource(filePath);
            bitmap = retriever.captureFrame();//我需要这个方法。

兄弟在做视频缩略图吧.这个类被@hide了.没有替代类吧...
我的方法是:改frameworks  MediaMetadataRetriever.java中@hide标签去掉,在current.xml中添加MediaMetadataRetriever到可用.重新编译frameworks,应用就可以调用到MediaMetadataRetriever这个类了...

是不是/frameworks/base/api/current.xml这个current.xml呢?这个current.xml是不是用了表明ap层可以调用frameworks的api呢
英文网站
1 http://www.speak2me.cn/main/home/cn/member_register.phpuid=298073&code=3276d4b9d5a5fdfaa7b2021c3de55995
练习口语和听力
2   http://www.24en.com/   我学习外贸英语的地方                                                   
3   http://www.putclub.com/   去练习听力的地方
4   http://bbs.china.tootoo.com/?wt.mc_id=SEbaiduWM&WT.srch=1  一个外贸社区,我学习外贸基础的地方
5. 英文锁定 www.Icansay.com/
6. 英语中国 www.englishchina.com/
7. 英语在线 www.englishabc.com/
8. 天天英语 english.chinaschool.net/
9. 时尚英语 www.oh100.com/huayuan/english/
10. 网络英语 www.englishlover.net/

11. 英语麦当劳 english23.6to23.com/
12. 搜狐在线学习 learning.sohu.com/lan/
13. 新东方教育在线 www.neworiental.org/
14. 中华网英语在线 edu.china.com/zh_cn/elearn/index.html
15. 庄子英语频道 www.zhuangzi.com/en/index.asp
16. English Town www.englishtown.com/master/home/
17. 洪恩在线 www.hongen.com/eng/index.htm
18. 空中英语教室 www.studioclassroom.com
19. 英语直通车 www.englishfree.com.cn/
20. 英语辅导报 http://www.ecp.com.cn/ecp/index.htm 
批处理
批处理(Batch),也称为批处理脚本。顾名思义,批处理就是对某对象进行批量的处理。批处理文件的扩展名为bat 。目前比较常见 的批处理包含两类:DOS批处理和PS批处理。PS批处理是基于强大的图片编辑软件Photoshop的,用来批量处理图片的脚本;而DOS批处理则是基于DOS命令的,用来自动地批量地执行DOS命令以实现特定操作的脚本。

目录

    DOS批处理释义
    简单批处理命令简介

       1. echo 命令
       2. rem 命令
       3. pause 命令
       4. call 命令
       5. start 命令
       6. goto 命令
       7. set 命令

    批处理符号简介

       1. 回显屏蔽 @
       2. 重定向1 >与>>
       3. 重定向2 <
       4. 管道符号 |
       5. 转义符 ^
       6. 逻辑命令符

    常用DOS命令释义

       1. 文件夹管理
       2. 文件管理
       3. 网络命令
       4. 系统管理

    语句结构释义

       1. if语句(选择结构)
       2. for语句(循环结构)

    字符串处理释义

       1. 1.截取字符串
       2. 2.替换字符串
       3. 3.字符串合并
       4. 4.扩充字符串

    数值计算简介
    批处理概念方法简介

       1. (二)用户变量
       2. (六)ASCII码

    批处理文件的应用

    DOS批处理释义
    简单批处理命令简介

       1. echo 命令
       2. rem 命令
       3. pause 命令
       4. call 命令
       5. start 命令
       6. goto 命令
       7. set 命令

    批处理符号简介

       1. 回显屏蔽 @
       2. 重定向1 >与>>
       3. 重定向2 <
       4. 管道符号 |
       5. 转义符 ^
       6. 逻辑命令符

    常用DOS命令释义

       1. 文件夹管理
       2. 文件管理
       3. 网络命令
       4. 系统管理

    语句结构释义

       1. if语句(选择结构)
       2. for语句(循环结构)

    字符串处理释义

       1. 1.截取字符串
       2. 2.替换字符串
       3. 3.字符串合并
       4. 4.扩充字符串

    数值计算简介
    批处理概念方法简介

       1. (二)用户变量
       2. (六)ASCII码

        * 批处理文件的应用

展开

编辑本段DOS批处理释义
  批处理是一种简化的脚本语言,也称作宏。它应用于DOS和Windows系统中,它是由DOS或者Windows系统内嵌的命令解释器(通常是COMMAND. COM或者CMD.EXE)解
   [批处理文件图标]

批处理文件图标
释运行。类似于Unix中的Shell脚本。批处理文件具有.bat或者.cmd的扩展
   [批处理窗口]

批处理窗口
名,其最简单的例子,是逐行书写在命令行中会用到的各种命令。更复杂的情况,需要使用if,for,goto等命令控制程序的运行过程,如同C,Basic等中高级语言一样。如果需要实现更复杂的应用,利用外部程序是必要的,这包括系统本身提供的外部命令和第三方提供的工具或者软件。批处理文件,或称为批处理程序,是由一条条的DOS命令组成的普通文本文件,可以用记事本直接编辑或用DOS命令创建,也可以用DOS下的文本编辑器Edit.exe 来编辑。在“命令提示”下键入批处理文件的名称,或者双击该批处理文件,系统就会调用Cmd.exe运行该批处理程序。一般情况下,每条命令占据一行;当然也可以将多条命令用特定符号(如:&、&&、|、||等)分隔后写入同一行中;还有的情况就是像if、for等较高级的命令则要占据几行甚至几十几百行的空间。系统在解释运行批处理程序时,首先扫描整个批处理程序,然后从第一行代码开始向下逐句执行所有的命令,直至程序结尾或遇见exit命令或出错意外退出。
编辑本段简单批处理命令简介
echo 命令
  打开回显或关闭请求回显功能,或显示消息。如果没有任何参数,echo
   [批处理与联机处理]

批处理与联机处理
命令将显示当前回显设置。
  语法
  echo [{on|off}] [message]
  Sample:@echo off / echo hello world
  在实际应用中我们会把这条命令和重定向符号(也称为管道符号,一般用> >> ^)结合来实现输入一些命令到特定的文件中。
rem 命令
  注释命令,类似于在C语言中的/*--------*/,它并不会被执行,只是起一个注释的作用,便于别人阅读
  和你自己日后修改。
  :: 也具有rem的功能
  但::和rem还是有区别的,当关闭回显时,rem和::后的内容都不会显示。
   [顺序批处理]

顺序批处理
但是当打开回显时,rem和rem
  后的内容会显示出来,然而::后的内容仍然不会显示。
  Rem Message
  Sample:@Rem Here is the description.
pause 命令
  暂停命令。运行 Pause 命令时,将显示下面的消息:
  Press any key to continue. . .(或:请按任意键继续. . .)
  Sample:
  @echo off
  :begin
  copy G:*.* d:\back
  echo 请插入另一张光盘...
  pause
  goto begin
  在这个例子中,驱动器 G 中磁盘上的所有文件均复制到d:\back中。显示的注释提示您将另一张光盘
  盘放入驱动器 G 时,pause 命令会使程序挂起,以便您更换光盘,然后按任意键继续处理。
call 命令
  从一个批处理程序调用另一个批处理程序,并且不终止父批处理程序。call
   [批处理功能界面]

批处理功能界面
命令接受用作调用目标的
  标签。如果在脚本或批处理文件外使用 Call,它将不会在命令行起作用。
  语法
  call [[Drive:][Path] FileName ] [:label [arguments]]
  参数
  [Drive:}[Path] FileName
  指定要调用的批处理程序的位置和名称。
start 命令
  调用外部程序,所有的DOS命令和命令行程序都可以由start命令来调用。
  如:start calc.exe即可打开Windows的计算器。
  常用参数:
  MIN 开始时窗口最小化
  SEPARATE 在分开的空间内开始 16 位 Windows 程序
  HIGH 在 HIGH 优先级类别开始应用程序
  REALTIME 在 REALTIME 优先级类别开始应用程序
  WAIT 启动应用程序并等候它结束
  parameters 这些为传送到命令/程序的参数
  执行的应用程序是 32-位 GUI 应用程序时,CMD.EXE 不等应用程序
   [常见批处理案例]

常见批处理案例
终止就返回命令提示。如果在命令
  脚本内执行,该新行为则不会发生。
goto 命令
  跳转命令。程序指针跳转到指定的标签,从标签后的第一条命令开始继续执行批处理程序。
  语法:goto label (label是参数,指定所要转向的批处理程序中的行。)
  Sample:
  if {%1}=={} goto noparms
  if {%2}=={} goto noparms(如果这里的if、%1.%2你不明白的话,先跳过去,后面会有详细的解释
  。)
  @Rem check parameters if null show usage
  :noparms
  echo Usage: monitor.bat ServerIP PortNumber
  goto end
  标签的名字可以随便起,但是最好是有意义的字母啦,字母前加个:
   [选择批处理]

选择批处理
用来表示这个字母是标签,goto
  命令就是根据这个:来寻找下一步跳到到那里。最好有一些说明这样你别人看起来才会理解你的意图啊。
set 命令
  显示、设置或删除变量。
  显示变量:set 或 set s 前者显示批处理当前已定义的所有变量及其值,后者显示所有以s开头的变量及值。
  设置变量:set aa=abcd 此句命令便可向变量aa赋值abcd。如果变量aa已被定义,则aa的值被修改为abcd;若aa尚未定义,则此句命令即可定义新的变量aa,同时为变量aa赋予初始值abcd。
  删除变量:set aa= 此句命令即可删除变量aa。若变量aa已被定义,则删除变量aa;若aa尚未定义,则此句命令无实质意义。
  需要说明的是,批处理中的变量是不区分类型的,不需要像C语言中的变量那样还要区分int、float、char等。比如执行set aa=345后,变量aa的值既可以被视为数字345,也可以被视为字符串345。
  set命令具有扩展功能,如用作交互输入、字符串处理、数值计算等,属于高级命令范畴。
编辑本段批处理符号简介
回显屏蔽 @
  表示不显示@后面的命令,在入侵过程中(例如使用批处理来格式化敌人
   [批处理过程]

批处理过程
的硬盘)自然不能让对方看到你使用的命令啦。
  @用法举例:通过运行批处理文件对比pause和@pause命令即可明了@的效果。
重定向1 >与>>
  将输出信息重定向到指定的设备或文件。系统默认输出到显示器。
  如:echo aaaaa>a.txt 即可将本在显示器上显示的信息aaaaa输出到文件a.txt中,屏幕上没有任何显示。如果文件a.txt本来已经存在,该命令将首先擦除a.txt中的所有信息,然后写入信息aaaaa;若a.txt本来就不存在,该命令即可新建一个a.txt文件,并写入信息aaaaa。
  echo aaaaa>>a.txt 类似于echo aaaaa>a.txt。区别在于:如果a.txt本已存在,>a.txt会擦除a.txt中的原有内容,而>>a.txt并不擦除原有内容,仅在a.txt文件的末尾添加信息aaaaa。a.txt不存在时,二者没有差别。
重定向2 <
  将输入信息来源重定向为指定的设备或文件。系统默认从显示器读取输入信息。
  重定向使用举例:
  =========================================
  @echo off
  echo abcdefg——这是文件a.txt中的信息>a.txt
  echo 请任意输入字符,以回车结束:
  set /p ifo=
  cls
  echo 【 从屏幕获得的输入信息 】
  echo %ifo%
  set /p ifo=<a.txt
  echo 【 从文件a.txt获得的输入信息 】
  echo %ifo%
  pause>nul
  =========================================
  读者观察命令与输出即可体会到重定向的功能和效果。
管道符号 |
  将管道符号前面命令的输出结果重定向输出到管道符号后面的命令中去,作为后面命令的输入。使用格式为:command_1|command_2
  管道符号使用举例:
  =========================================
  @echo off
  echo aaaa>a.txt
  del /p a.txt
  pause
  =========================================
  @echo off
  echo aaaa>a.txt
  echo y|del /p a.txt
  pause
  =========================================
  对比以上两个批处理执行结果,读者即可明白管道符的用法和效果。
  需要说明的是,上面del命令添加开关/p只是为了让读者明白管道符号的使用方法,实际删除文件时不加/p开关即可实现无提示直接删除。
转义符 ^
  将特殊符号转化为一般符号,即剥离特殊符号的特殊地位。特殊符号指:| & > <
  比如,如果我们想输出符号“>”,直接用命令 echo > 是不行的,必须修改为 echo ^> 。其余几个特殊符号类似需要有同样的处理。
  转义字符使用举例:
  =========================================
  @echo off
  echo aaaa>a.txt
  echo 第一句echo执行完毕
  echo aaaa^>a.txt
  echo 第二句echo执行完毕
  pause
  =========================================
  比较上面的两句echo,第一句echo将信息aaaa输出到了文件a.txt,而第二句echo则在直接屏幕上显示出aaaa>a.txt
逻辑命令符
  逻辑命令符包括:&、&&、||
  &-它的作用是用来连接n个DOS命令,并把这些命令按顺序执行,而不管是否有命令执行失败;
  &&-当&&前面的命令成功执行时,执行&&后面的命令,否则不执行;
  ||-当||前面的命令失败时,执行||后面的命令,否则不执行。
  =========================================
  @echo off
  echo ^|^|
  reg add HKCU /v try /f||echo **失败**
  reg add HKCU1 /v try /f||echo **成功**
  echo ^&^&
  reg delete HKCU /v try /f&&echo **成功**
  reg delete HKCU /v try /f&&echo **失败**
  echo ^&
  reg delete HKCU /v try /f&echo **也许成功**
  reg delete HKCU /v try /f&echo **也许失败**
  pause
  =========================================
  执行reg add或reg delete后,系统会给出执行结果;我们通过echo命令也给出了“执行结果”。对比系统和我们自己给出的结果,既可以验证逻辑命令的判断机理。
编辑本段常用DOS命令释义
文件夹管理
  cd 显示当前目录名或改变当前目录。
  md 创建目录。
  rd 删除一个目录。
  dir 显示目录中的文件和子目录列表。
  tree 以图形显示驱动器或路径的文件夹结构。
  path 为可执行文件显示或设置一个搜索路径。
  copy 复制文件和目录树。
文件管理
  type 显示文本文件的内容。
  copy 将一份或多份文件复制到另一个位置。
  del 删除一个或数个文件。
  move 移动文件并重命名文件和目录。(Windows XP Home Edition中没有)
  ren 重命名文件。
  replace 替换文件。
  attrib 显示或更改文件属性。
  find 搜索字符串。
  fc 比较两个文件或两个文件集并显示它们之间的不同
网络命令
  ping 进行网络连接测试、名称解析
  ftp 文件传输
  net 网络命令集及用户管理
  telnet 远程登陆
  ipconfig显示、修改TCP/IP设置
  msg 给用户发送消息
  arp 显示、修改局域网的IP地址-物理地址映射列表
系统管理
  at 安排在特定日期和时间运行命令和程序
  shutdown立即或定时关机或重启
  tskill 结束进程
  taskkill结束进程(比tskill高级,但WinXPHome版中无该命令)
  tasklist显示进程列表(Windows XP Home Edition中没有)
  sc 系统服务设置与控制
  reg 注册表控制台工具
  powercfg控制系统上的电源设置
  对于以上列出的所有命令,在cmd中输入命令+/?即可查看该命令的帮助信息。如find /?
编辑本段语句结构释义
  类似于C语言,批处理也有它的语句结构。批处理的语句结构主要有选择结构(if语句)、循环结构(for语句)等。
if语句(选择结构)
  if语句实现条件判断,包括字符串比较、存在判断、定义判断等。通过条件判断,if语句即可以实现选择功能。
  1.字符串比较
  if语句仅能够对两个字符(串)是否相同、先后顺序进行判断等。其命令格式为:
  IF [not] string1 compare-op string2 command1 [else command2]
  其中,比较操作符compare-op有以下几类:
  == - 等于
  EQU - 等于
  NEQ - 不等于
  LSS - 小于
  LEQ - 小于或等于
  GTR - 大于
  GEQ - 大于或等于
  选择开关/i则不区分字符串大小写;选择not项,则对判断结果进行逻辑非。
  字符串比较示例:
  ===============================================
  @echo off
  set str1=abcd1233
  set str2=ABCD1234
  if %str1%==%str2% (echo 字符串相同!) else (echo 字符串不相同!)
  if /i %str1% LSS %str2% (echo str1^<str2) else (echo str1^>=str2)
  echo.
  set /p choice=是否显示当前时间?(y/n)
  if /i not %choice% EQU n echo 当前时间是:%date% %time%
  pause>nul
  ===============================================
  对于最后一个if判断,当我们输入n或N时的效果是一样的,都不会显示时间。如果我们取消开关/i,则输入N时,依旧会显示时间。
  另外请注意一下几个细节:1-echo str1^<str2和echo str1^>=str2;2-echo.。
  2.存在判断
  存在判断的功能是判断文件或文件夹是否存在。其命令格式为:
  IF [NOT] EXIST filename command1 [else command2]
  ===============================================
  @echo off
  if exist %0 echo 文件%0是存在的!
  if not exist %~df0 (
  echo 文件夹%~df0不存在!
  ) else echo 文件夹%~df0存在!
  pause>nul
  ===============================================
  这里注意几个地方:
  1-存在判断既可以判断文件也可以判断文件夹;
  2-%0即代表该批处理的全称(包括驱动器盘符、路径、文件名和扩展类型);
  3-%~df0是对%0的修正,只保留了其驱动器盘符和路径,详情请参考for /?,属高级批处理范畴;
  4-注意if语句的多行书写,多行书写要求command1的左括号必须和if在同一行、 else必须和command1的右括号同行、command2的左括号必须与else同行、command1和command2都可以有任意多行,即 command可以是命令集。
  3.定义判断
  定义判断的功能是判断变量是否存在,即是否已被定义。其命令格式为:
  IF [not] DEFINED variable command1 [else command2]
  存在判断举例:
  ===============================================
  @echo off
  set var=111
  if defined var (echo var=%var%) else echo var尚未定义!
  set var=
  if defined var (echo var=%var%) else echo var尚未定义!
  pause>nul
  ===============================================
  对比可知,"set var="可以取消变量,收回变量所占据的内存空间。
  4.结果判断
  masm %1.asm
  if errorlevel 1 pause & edit %1.asm
  link %1.obj
  先对源代码进行汇编,如果失败则暂停显示错误信息,并在按任意键后自动进入编辑界面;否则用link程序连接生成的obj文件,这种用法是先判断前一个命令执行后的返回码(也叫错误码,DOS程序在运行完后都有返回码),如果和定义的错误码符合(这里定义的错误码为1),则执行相应的操作(这里相应的操作为pause & edit %1.asm部分)。
  另外,和其他两种用法一样,这种用法也可以表示否定。用否定的形式仍表达上面三句的意思,代码变为:
  masm %1.asm
  if not errorlevel 1 link %1.obj
  pause & edit %1.asm
for语句(循环结构)
  for语句可以实现类似于C语言里面的循环结构,当然for语句的功能要更强大一点,通过不同的开关可以实现更多的功能。for语句有多个开关,不同开关将会实现不同的功能。
  1.无开关
  无开关的for语句能够对设定的范围内进行循环,是最基本的for循环语句。其命令格式为:
  FOR %%variable IN (set) DO command
  其中,%%variable是批处理程序里面的书写格式,在DOS中书写为%variable,即只有一个百分号(%);set就是需要我们设定的循环范围,类似于C语言里面的循环变量;do后面的command就是循环所执行的命令,即循环体。
  无开关for语句举例:
  ===============================================
  @echo off
  for %%i in (a,"b c",d) do echo %%i
  pause>nul
  ===============================================
  2.开关/L
  含开关/L的for语句,可以根据set里面的设置进行循环,从而实现对循环次数的直接控制。其命令格式为:
  FOR /L %%variable IN (start,step,end) DO command
  其中,start为开始计数的初始值,step为每次递增的值,end为结束值。当end小于start时,step需要设置为负数。
  含开关/L的for语句举例(创建5个文件夹):
  ===============================================
  @echo off
  for /l %%i in (1,2,10) do md %%i
  pause
  ===============================================
  上例将新建5个文件夹,文件夹名称依次为1.3.5.7.9。可以发现,%%i的结束值并非end的值10,而是不大于end的一个数。
  3.开关/F
  含开关/F的for语句具有最强大的功能,它能够对字符串进行操作,也能够对命令的返回值进行操作,还可以访问硬盘上的ASCII码文件,比如txt文档等。其命令格式为:
  FOR /F ["options"] %%variable IN (set) DO command
  其中,set为("string"、'command'、file-set)中的一个;options是(eol=c、skip=n、delims=xxx、tokens=x,y,m-n、usebackq)中的一个或多个的组合。各选项的意义参见for /f。一般情况下,使用较多的是skip、tokens、delims三个选项。
  含开关/F的for语句举例:
  ===============================================
  @echo off
  echo **No Options:
  for /f %%a in ("1,2,10") do echo a=%%a
  echo **Options tokens ^& delims:
  for /f "tokens=1-3 delims=," %%a in ("1,2,10") do echo a=%%a b=%%b c=%%c
  pause
  ===============================================
  @echo off
  echo 本文件夹里面的文件有:
  for /f "skip=5 tokens=3* delims= " %%a in ('dir') do (
  if not "%%a"=="<DIR>" if not "%%b"=="字节" if not "%%b"=="可用字节" echo %%b
  )
  pause
  ===============================================
  @echo off
  echo 本文件夹里面的文件有:
  dir>c:\file.txt
  for /f "skip=5 tokens=3* delims= " %%a in (c:\file.txt) do (
  if not "%%a"=="<DIR>" if not "%%b"=="字节" if not "%%b"=="可用字节" echo %%b
  )
  del c:\file.txt
  pause
  ===============================================
  对于后面的两个例子,其中options里面的delims= 是可以删除的,因为只要添加了/F开关系统就将delims的值默认为空格。
  符号字符串中的最后一个字符星号,
  那么额外的变量将在最后一个符号解析之后
  分配并接受行的保留文本。本例中也可以改为4,不过文件名中有空格的文件,只能显示空格以前部分
  同时我们也看到了,for语句的do后面的command也是可以分行的,只需要保证command的左括号和do在同一行就可以了。
  4.开关/D或/R
  含开关/D或/R的for语句是与目录或文件有关的命令,一般情况下很少使用。含开关/R的命令有时候被用于通过遍历文件夹来查找某一个文件或文件夹,故而列举此例。
  含开关/R的for语句举例(文件夹遍历):
  ===============================================
  @echo off
  setlocal enabledelayedexpansion
  FOR /R d: %%i IN (.) DO (
  set dd=%%i
  set "dd=!dd:~0,-1!"
  echo !dd!
  )
  pause
  exit
  ===============================================
  上例即可以罗列出D盘下的所有文件夹,其速度要比命令"tree d:"慢多了,不过其返回结果的实用性则远远超过了tree命令。
  一般情况下我们不推荐通过遍历文件夹来查找文件,特别是在查找某些程序(比如QQ.exe)的位置时。推荐通过reg命令查找注册表来查找QQ的路径,以保证查找效率。
  上例中也出现了几个新面孔,如setlocal、感叹号等。其中,感叹号其实就是变量百分号 (%)的强化版。之所以要用!而不用%,是因为在for循环中,当一个变量被多次赋值时,%dd%所获取的仅仅是dd第一次被赋予的值;要想刷新dd的值,就必须首先通过命令"setlocal enabledelayedexpansion"来开启延迟变量开关,然后用!dd!来获取dd的值。
  for语句是批处理里面功能最强大、使用最普遍却又最难掌握的一套命令,这也是批处理菜鸟和批处理高手最明显的一个分水岭,一旦掌握了这套命令,那么你就离批处理达人不远了!
编辑本段字符串处理释义
  批处理有着具有非常强大的字符串处理能力,其功能绝不低于C语言里面的字符串函数集。批处理中可实现的字符串处理功能有:截取字符串内容、替换字符串特定字段、合并字符串、扩充字符串等功能。下面对这些功能一一进行讲解。
1.截取字符串
  截取字符串可以说是字符串处理功能中最常用的一个子功能了,能够实现截取字符串中的特定位置的一个或多个字符。举例说明其基本功能:
  =========================================
  @echo off
  set ifo=abcdefghijklmnopqrstuvwxyz0 12 3456789
  echo 原字符串(第二行为各字符的序号):
  echo %ifo%
  echo 1234567890 123 45678901234567890123456
  echo 截取前5个字符:
  echo %ifo:~0,5%
  echo 截取最后5个字符:
  echo %ifo:~-5%
  echo 截取第一个到倒数第6个字符:
  echo %ifo:~0,-5%
  echo 从第4个字符开始,截取5个字符:
  echo %ifo:~3,5%
  echo 从倒数第14个字符开始,截取5个字符:
  echo %ifo:~-14,5%
  pause
  =========================================
  当然,上面的例子只是将字符串处理的基本功能展示出来了,还看不出字符串处理具体有什么用处。下面这个例子是对时间进行处理。
  =========================================
  @echo off
  echo 当前时间是:%time% 即 %time:~0,2%点%time:~3,2%分%time:~6,2%秒%time:~9,2%厘秒
  pause
  =========================================
2.替换字符串
  替换字符串,即将某一字符串中的特定字符或字符串替换为给定的字符串。举例说明其功能:
  =========================================
  @echo off
  set aa=伟大的中国!我为你自豪!
  echo 替换前:%aa%
  echo 替换后:%aa:中国=中华人民共和国%
  echo aa = %aa%
  set "aa=%aa:中国=中华人民共和国%"
  echo aa = %aa%
  pause
  =========================================
  对于上面的例子有一点说明,对比两个echo aa = %aa%可以发现,如果要修改变量aa的内容的话,就需要将修改结果“%aa:中国=中华人民共和国%”赋值给变量aa。上面的字符串截取也有着同样的特点。
3.字符串合并
  其实,合并字符串就是将两个字符串放在一起就可以了。举例说明:
  =========================================
  @echo off
  set aa=伟大的中国!
  set bb=我为你自豪!
  echo %aa%%bb%
  echo aa=%aa%
  echo bb=%bb%
  set "aa=%aa%%bb%"
  echo aa=%aa%
  pause
  =========================================
  同样,如果要改变变量aa的内容的话,就需要将合并结果“%aa%%bb%”赋值给变量aa。
4.扩充字符串
  “扩充”这个词汇来自于微软自己的翻译,意思就是对表示文件路径的字符串进行特殊的处理,具体功能罗列如下:
  =========================================
  ~I - 删除任何引号("),扩充 %I
  %~fI - 将 %I 扩充到一个完全合格的路径名
  %~dI - 仅将 %I 扩充到一个驱动器号
  %~pI - 仅将 %I 扩充到一个路径
  %~nI - 仅将 %I 扩充到一个文件名
  %~xI - 仅将 %I 扩充到一个文件扩展名
  %~sI - 扩充的路径只含有短名
  %~aI - 将 %I 扩充到文件的文件属性
  %~tI - 将 %I 扩充到文件的日期/时间
  %~zI - 将 %I 扩充到文件的大小
  %~$PATH:I - 查找列在路径环境变量的目录,并将 %I 扩充
  到找到的第一个完全合格的名称。如果环境变量名
  未被定义,或者没有找到文件,此组合键会扩充到
  空字符串
  可以组合修饰符来得到多重结果:
  %~dpI - 仅将 %I 扩充到一个驱动器号和路径
  %~nxI - 仅将 %I 扩充到一个文件名和扩展名
  %~fsI - 仅将 %I 扩充到一个带有短名的完整路径名
  %~dp$PATH:i - 查找列在路径环境变量的目录,并将 %I 扩充
  到找到的第一个驱动器号和路径。
  %~ftzaI - 将 %I 扩充到类似输出线路的 DIR
  =========================================
  以上内容引用于for /?帮助信息。其中的I代表变量I,不过需要说明的是,不是所有的变量都能够进行扩充的,有两个条件:1.该字符串代表一个文件路径;2.变量要用%x来表示,x可取a-z A-Z 0-9共62个字符中的任意一个。举例说明:
  =========================================
  @echo off
  echo 正在运行的这个批处理:
  echo 完全路径:%0
  echo 去掉引号:%~0
  echo 所在分区:%~d0
  echo 所处路径:%~p0
  echo 文件名:%~n0
  echo 扩展名:%~x0
  echo 文件属性:%~a0
  echo 修改时间:%~t0
  echo 文件大小:%~z0
  pause
  =========================================
  其中的%0是批处理里面的参数,代表当前运行的批处理的完全路径。类似的还有%1-%9,分别代表传递来的第1-9个参数。例子如下:
  ===============================================
  @echo off
  set aa=C:\Windows\PPP\a.btx
  call :deal aaa %aa% "c c" ddd eee
  pause>nul
  exit
  :deal
  echo %%0 = %0
  echo %%1 = %1
  echo %%2 = %2
  echo %%3 = %3
  echo %%4 = %4
  echo %%5 = %5
  ===============================================
  其中,变量aa在之前是不可以扩充的,通过call命令并将aa作为参数传递给子函数:deal,将aa变量转换成了变量%1,即符合%x格式,从而可以进行字符串扩充。
  至于%x中x取a-z A-Z的形式,可以复习一下for语句,for语句里面的变量就是用%x来表示的,因而可以直接进行扩充。
编辑本段数值计算简介
  批处理里面的数值计算功能较弱,只能够进行整型计算,忽略浮点数的小数部分;同时数值计算的范围也受限于系统位数,对于目前较为常见的32位机来说,数值计算能处理的数值范围为0x80000000h~0x7FFFFFFFh,即-2147483648~+2147483647。
  数值计算需要使用set命令,具体格式为“set /a expression”。其中,expression代表计算表达式,计算表达式跟C语言里面的表达式基本上完全一致。set支持的运算符也跟C语言里面的一样,只是没有了増一减一。set支持的运算符及优先级排序如下:
  =========================================
  () - 分组
  ! ~ - - 一元运算符(逻辑非、按位非、取负)
  * / % - 算数运算符(乘、除得商、除得余数,即取余)
  + - - 算数运算符(加、减)
  << >> - 逻辑移位(左移一位、右移一位)
  & - 按位“与”
  ^ - 按位“异”
  | - 按位“或”
  = *= /= %= += -= - 赋值
  &= ^= |= <<= >>=
  , - 表达式分隔符(set可一次处理多个表达式)
  =========================================
  我们知道,批处理中取变量的值是需要用%或者!的,而在set /a 中,直接用变量名称即可取得变量的值。另外,set支持八进制(数字前缀0)、十进制(数字无前缀)和十六进制(数字前缀0x),且支持不同进制之间的计算,如set /a a=123+0123+0x123,计算及显示结果为十进制。
编辑本段批处理概念方法简介
  学习有关批处理的一些比较琐碎、但又比较有价值的一些知识,运用这些知识后,才有条件更好的实现的批处理的功能,并能使批处理摆脱黑白世界而显得更加多姿多彩。
  (一)环境变量 所谓“环境变量”,实际上就是C语言里面的“变量”的意思。批处理的变量可以分为两类,由系统定义的系统变量和由用户根据需要自定义的用户变量。
  【 1.系统变量 】
  批处理的一些变量是由操作系统事先定义好的,可以适用于任何批处理,我们称这些特殊的变量为“系统变量”。系统变量有很多个,包括硬件类、操作系统类、文件路径类、系统时间类等。要查看所有的系统变量,请新打开一个cmd窗口,输入set回车即可。对几个比较常用的变量解释如下:
  ComputerName 计算机名,即右键 我的电脑--属性--计算机名 选项卡中的“完整的计算机名称”
  ComSpec cmd.exe完整路径名
  HomeDrive 系统分区盘符,一般都是C盘,故而HomeDrive=C:
  Path 可执行文件默认搜索路径。这个东东非常重要!待会儿单独讲解…
  ProgramFiles 就是系统的Program Files的路径啦,一般都是C:\Program Files,这就是安装软件时默认的安装路径了
  Prompt 个性化设置cmd提示符的必备武器!不过,我没怎么用过~
  SystemDrive 包含系统根目录的分区,其实就是HomeDrive了
  SystemRoot 系统根目录路径,一般都是C:\WINDOWS
  Temp、Tmp 文件、程序等可使用的临时目录,默认是C:\WINDOWS\Temp或Tmp。几乎所有的程序在运行时都会在这个目录里面“临时”写入文件。一般情况下,程序写入的临时文件都应该被该程序删除,可惜的是,大部分的程序都很健忘,导致这个文件夹占据的空间越来越大,自然也就使我们的系统增肥喽。所以,我们要把它修改到其他分区,并且时时的清理里面的临时文件。
  UserName 当前用户名,即所登陆的账户名
  UserProfile 当前用户的配置目录,一般都是C:\Documents and Settings\%UserName%。默认情况下,我们的桌面就是这个目录下面的“桌面”文件夹;我的文档就是这个目录下面的“My Documents”文件夹。所以啦,往桌面上或我的文档里面放东西就是放到这个文件夹下面了,也就是放到C盘了,重装系统时要覆盖C盘内容的,所以桌面上或我的文档里面的东西当然就会Gone with the Wind了~解决方法有两个,一是保持良好的习惯,不把重要文件放到这两个地方;二是,修改默认设置,将这两个文件夹都移到其他分区。
  WinDir 操作系统路径,其实就是SystemRoot了
(二)用户变量
  编写批处理程序时,用户根据需要自己定义的变量称之为用户变量。用户变量类似于C语言里面的变量,仅仅在定义该变量的程序中有效。
  用户变量由set命令定义,这是批处理中非常非常重要的一个操作,从而使set命令成为批处理里面使用频率最高的几个命令之一。关于set命令的使用,参考set /?,本教程也会在后面对其进行讲解。
  【 3.变量引用 】
  前面的几节课里面,我们已经看到了如何引用变量,即直接用变量名操作变量,通过"%"或"!" 来获取变量的值。其中,只有在for语句里面重复对同一变量多次赋值时才需要使用"!",并且在使用"!"调用变量时,要首先“启用延迟环境变量扩充”,启动命令为:SetLocal EnableDelayedExpansion。另外需要说明的是,“启用延迟环境变量扩充”后,所有的"!"都将被视为“取变量值”的特殊符号,即使用"^!"也不能输出符号"!"。若要输出"!",则需要“停用延迟环境变量扩充”,命令为:SetLocal DisableDelayedExpansion
  (三)参数 
  跟C语言类似,在调用函数或其他批处理时可能需要传递参数。批处理的参数传递分为直接和间接两种传递参数的方法。
  【 1.直接传递 】
  直接传递参数,即在使用call命令时,不使用任何参数,在子函数或子批处理里面直接对主函数(也称父批处理)里面的变量进行修改。这跟汇编语言里面的参数传递方式类似。
  直接传递参数举例:
  ===============================================
  @echo off
  setlocal enabledelayedexpansion
  set var=aCdehiM,?mnrstW y
  echo %var%
  call :deal
  setlocal disabledelayedexpansion
  set var=%var:?=!%
  echo %var%
  pause>nul
  exit
  :deal
  set tm=!var!
  set var=
  for %%i in (6,3,11,11,16,15,1,4,11,5,12,13,9,0,12,7,15,14,5,10,2,16,18,8) do (
  set var=!var!!tm:~%%i,1!
  )
  goto :eof
  ===============================================
  可以发现,当我们把变量var作为参数赋予子函数:deal后,子函数对var的值进行了修改;当子函数返回后,主函数里面的var的值就已经是子函数里面var被修改后的值了。
  该例子中,使用了本节课前面讲到的setlocal enabledelayedexpansion和setlocal disabledelayedexpansion,前者保证了var在for循环里面能够根据我们的意愿进行处理,后者保证了能够正确输出符号"!"。另外例子中还使用了命令set,利用set对字符串进行了处理。还有一个地方使用了语句goto :eof,该语句相当于C语言里面的return或汇编语言里面的RET,即子程序返回命令。需要说明的是,当子函数本身就在批处理文件的末尾的话,我们是可以省略这句话的,比如将此例的goto :eof删除是不会产生任何影响的。
  【 2.间接传递 】
  间接传递参数,即在使用call命令时,在其后面添加参数,形如call {[:label][ChildBatch]} Parameter1 Parameter2 ... ParameterN。这跟C语言里面传递参数的格式类似。不同于C语言,批处理中的子函数不需要定义形参,更不需要指定参数的个数。传递过来的参数,在子函数或子批处理里面是以%1~%9的形式表示的,即%1~%9分别表示传递过来的第1~9个参数。
  ===============================================
  @echo off
  call :deal aaa bbb "c c" ddd eee
  pause>nul
  exit
  :deal
  echo %%0 = %0
  echo %%1 = %1
  echo %%2 = %2
  echo %%3 = %3
  echo %%4 = %4
  echo %%5 = %5
  ===============================================
  通过这个例子就可以清晰的看到%n参数表示法的用法。参数列表中包含空格的依旧要用双引号(")引起来;另外,也可以看到,%0已经变成了子函数的标号了,而不是父批处理的文件名全称。
  【 3.区别 】
  这两种参数传递方法本质上是没有区别的,形式上,直接传递直接对原变量进行操作,丢失了原变量的值;间接传递则通过%n对原变量进行了简单的备份,并且通用性更强,即不限定原变量的名称。另外,使用%n还有一个非常大的好处,就是可以通过%~*i 来加强处理变量的能力。关于%~*i,详细内容参见for /?。
  针对二者的差别,可以根据情况决定使用哪种传递方式:
  --1.作为参数的变量名固定、且在子函数中不需要对其进行备份的情况下,使用直接传递法;
  --2.若将子函数作为一个通用的程序模块,以适应于对不同变量的处理,或者作为参数的变量不需要备份时,使用间接传递法。
  具体使用哪种方法,还需根据实际情况或使用习惯进行选择。
  (四)返回值 有些命令在执行之后将会返回一定的错误值(errorlevel),可以通过errorlevel的值判断命令执行的状况。这点类似于C语言里面的exit(num),num就是错误代码。
  获取返回值errorlevel的方法就是,在执行命令后,立马调用返回值errorlevel,如echo %errorlevel%或者if %errorlevel%==1等命令。
  errorlevel举例:
  ===============================================
  @echo off
  reg add HKCU /v try /f>nul
  reg delete HKCU /v try /f
  if errorlevel 0 (echo  删除成功!) else (echo 删除失败!)
  reg delete HKCU /v try /f
  if %errorlevel%==0 (echo 删除成功!) else (echo 删除失败!)
  pause>nul
  ===============================================
  上面例子中,由于第一成功的删除了注册表,导致第二次因为找不到注册表而宣告失败。同时我们也看到了errorlevel的使用方法,即if errorlevel 0和if %errorlevel%==0是一样的。也许你注意到了,里面还有个笑脸呢~O(∩_∩)O哈哈~这就是ASCII码啦,后面跟你讲啊…
  一般情况下,程序或命令成功执行时,返回的errorlevel是0,错误时返回1或更高的值。当然,有些命令是没有返回值的,这点需要注意。
  嗯,有没有想起前面有个类似的东西啊?对了,那就是||和&&了,这两个符号就是根据errorlevel的值来进行逻辑判断的~
  (五)用户交互 
  批处理,黑框白字是它最著名的特征。虽然当初DOS为人们使用计算机做出了莫大的贡献,但在 Windows盛行的今天,人们已经疏远并且惧怕那个黑色的窗口了。微软为了让先天有着批量处理“体力活”能力的DOS避免“冷酷”,便于接近,特意提供了几个小命令,加强批处理的用户交互功能。
  【 1.视窗 】
  首先我们要DIY它的窗口。使用命令:color、mode、msg。
  --1.设置窗口背景色和字体颜色by color。详细内容参见color /?。
  --2.设置窗口大小by "MODE CON [COLS=c] [LINES=n]",cols即宽度,lines即高度。
  --3.GUI交互窗口by msg。详细内容参见msg /?。
  视窗DIY举例:
  ===============================================
  @echo off
  set a=10
  set b=0
  :tex
  set /a a+=3
  set /a b+=1
  mode con cols=%a% lines=%b%
  if %a% lss 60 goto :tex
  echo O(∩_∩)O 圣诞快乐 O(∩_∩)O
  set a=15,a6,2e,d0,34,8b,4f,9d,5e
  for %%i in (%a%) do (
  ping -n 2 127.1>nul
  color %%i
  )
  for %%i in (%a%,%a%) do (
  ping -n 1 127.1>nul
  color %%i
  )
  >>ms.txt echo */. . . * .
  >>ms.txt echo .\* . [] * __
  >>ms.txt echo */ . ./\~~~~~~~~~~~~'\. ^|
  >>ms.txt echo \* ,/,..,\,...........,\.
  >>ms.txt echo ^|^| ..▎# ▎田 田 ▎ ^| ▎
  >>ms.txt echo ^|^| ^&^&▎ ▎ ▎'^|'▎ o
  >>ms.txt echo ^|^| ##■■■■■■■■■■〓
  msg %username% /w /time:3600 <ms.txt
  del ms.txt
  pause
  ===============================================
  【 2.声音 】
  呵呵,是不是注意到了批处理没有声音呐?闲话不说,直接做实验吧 ^_^
  ===============================================
  @echo off
  echo 做好准备,响了啊!!
  pause
  cls
  echo 
  echo 怎么样?呵呵,刺激吧~
  pause
  cls
  mshta vbscript:createobject("sapi.spvoice").speak("Merry Christmas and Happy New Year!")(window.close)
  pause
  ===============================================
  此处,这个就是调用vbs的方法:vbscript:createobject("sapi.spvoice").speak("Merry Christmas and Happy New Year!")
  speak 有人说如果tts安装了中文引擎是可以讲中文的。
  其实不然我测试了一下只要打上拼音(一个字和一个字的拼音中间需要加空格)就可以说中文啦~
  【 3.控制 】
  在运行批处理的过程中,我们如何干预批处理呢?呵呵,直接点叉叉就可以结束它了!嗯,不错,不过,太野蛮了~
  --1.暂停批处理:直接按键盘上的Pause键喽
  --2.终止批处理:组合键Ctrl+C。不过,有时候它好像响应的不太积极啊…
(六)ASCII码
  前面的例子中,我们已经使用过一次ASCII码了,也就是那个笑脸。ASCII码是图形化的符号,可以用来点缀我们的批处理的。
  在cmd窗口中我们可以通过任意一个字符的ASCII码来输入该字符,比如Ctrl+G、 Ctrl+N等,字母a-z对应ASCII码的97-122。对于ASCII码大于26的字符,可以通过这个方法来输入:按住Alt键不松,通过小键盘输入ASCII码的十进制值,松开Alt键即可。
编辑本段批处理文件的应用
  批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT。
  1. REM
  REM 是个注释命令一般是用来给程序加上注解的,该命令后的内容在程序执行的时候将不会被显示和执行。例:
  REM 你现在看到的就是注解,这一句将不会被执行。在以后的例子中解释的内容都REM 会放在REM后面。请大家注意。
  2. ECHO
  ECHO 是一个回显命令主要参数有OFF和 ON,一般用ECHO message来显示一个特定的消息 。例:
  Echo off
  Rem 以上代表关闭回显即不显示所执行的命令
  Echo 这个就是消息。
  Rem 以上代表显示“这就是消息”这列字符
  执行结果:
  C:\>ECHO.BAT
  这个就是消息。
  3. GOTO
  GOTO 即为跳转的意思。在批处理中允许以“:XXX”来构建一个标号然后用GOTO :标号直接来执行标号后的命令。例
  :LABEL
  REM 上面就是名为LABEL的标号。
  DIR C:\
  DIR D:\
  GOTO LABEL
  REM 以上程序跳转标号LABEL处继续执行。
  4.CALL
  CALL 命令可以在批处理执行过程中调用另一个批处理,当另一个批处理执行完后再继续执行原来的批处理。例:
  批处理2.BAT内容如下:
  ECHO 这就是2的内容
  批处理1.BAT内容如下:
  ECHO 这是1的内容
  CALL 2.BAT
  ECHO 1和2的内容全部显示完成
  执行结果如下:
  C:\>1.BAT
  这是1的内容
  这就是2的内容
  1和2的内容全部显示完成
  5.PAUSE
  PAUSE 停止系统命令的执行并显示下面的内容。例:
  C:\> PAUSE
  请按任意键继续 . . .
  6. IF
  IF 条件判断语句,语法格式如下:
  IF [NOT] ERRORLEVEL number command
  IF [NOT] string1==string2 command
  IF [NOT] EXIST filename command
  说明:
  [NOT] 将返回的结果取反值即“如果没有”的意思。
  ERRORLEVEL 是命令执行完成后返回的退出值
  Number 退出值的数字取值范围0~255。判断时值的排列顺序应该又大到小。返回的值大于或等于指定的值时条件成立。
  string1==string2 string1和string2都为字符的数据,英文字符的大小写将看做不同,这个条件中的等于号必须是2个(绝对相等),条件相等后即执行后面的 command
  EXIST filename 为文件或目录存在的意思。
  IF ERRORLEVEL这条语句必须放在某一个命令后面。执行命令后由IF ERRORLEVEL来判断命令的返回值。
Java解压zip和rar文件
一、解压rar文件。
由于WinRAR 是共享软件,并不是开源的,所以解压rar文件的前提是系统已经安装了winrar,比如本人的安装路径是:
C:\\Program Files\\WinRAR\\winrar.exe
然后运用java.lang.Process 的相关知识来运行系统命令行来实现解压的。
winrar 命令行相关参数自己可以搜索下的网上资料很多
具体代码: 
package common;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

/**
 * 解压rar文件(需要系统安装Winrar 软件)
 * @author Michael sun
 */
public class UnRarFile {
    /**
     * 解压rar文件
     * 
     * @param targetPath
     * @param absolutePath
     */
    public void unRarFile(String targetPath, String absolutePath) {

        try {

            // 系统安装winrar的路径
            String cmd = "C:\\Program Files\\WinRAR\\winrar.exe";
            String unrarCmd = cmd + " x -r -p- -o+ " + absolutePath + " "
                    + targetPath;

            Runtime rt = Runtime.getRuntime();
            Process pre = rt.exec(unrarCmd);
            InputStreamReader isr = new InputStreamReader(pre.getInputStream());
            BufferedReader bf = new BufferedReader(isr);
            String line = null;
            while ((line = bf.readLine()) != null) {
                line = line.trim();
                if ("".equals(line)) {
                    continue;
                }
                System.out.println(line);
            }

            bf.close();
        } catch (Exception e) {
            System.out.println("解压发生异常");
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        String targetPath = "D:\\test\\unrar\\";
        String rarFilePath = "D:\\test\\test.rar";
        UnRarFile unrar = new UnRarFile();
        unrar.unRarFile(targetPath, rarFilePath);
    }

}
二、解压zip文件
由于zip是免费的,所以在jdk里提供了相应的类对zip文件的实现:
java.util.zip.*,详细情况可以参考java API 
package common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * 解压zip文件
 * @author Michael sun
 */
public class UnzipFile {

    /**
     * 解压zip文件
     * 
     * @param targetPath
     * @param zipFilePath
     */
    public void unzipFile(String targetPath, String zipFilePath) {

        try {
            File zipFile = new File(zipFilePath);
            InputStream is = new FileInputStream(zipFile);
            ZipInputStream zis = new ZipInputStream(is);
            ZipEntry entry = null;
            System.out.println("开始解压:" + zipFile.getName() + "...");
            while ((entry = zis.getNextEntry()) != null) {
                String zipPath = entry.getName();
                try {

                    if (entry.isDirectory()) {
                        File zipFolder = new File(targetPath + File.separator
                                + zipPath);
                        if (!zipFolder.exists()) {
                            zipFolder.mkdirs();
                        }
                    } else {
                        File file = new File(targetPath + File.separator
                                + zipPath);
                        if (!file.exists()) {
                            File pathDir = file.getParentFile();
                            pathDir.mkdirs();
                            file.createNewFile();
                        }

                        FileOutputStream fos = new FileOutputStream(file);
                        int bread;
                        while ((bread = zis.read()) != -1) {
                            fos.write(bread);
                        }
                        fos.close();

                    }
                    System.out.println("成功解压:" + zipPath);

                } catch (Exception e) {
                    System.out.println("解压" + zipPath + "失败");
                    continue;
                }
            }
            zis.close();
            is.close();
            System.out.println("解压结束");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        String targetPath = "D:\\test\\unzip";
        String zipFile = "D:\\test\\test.zip";
        UnzipFile unzip = new UnzipFile();
        unzip.unzipFile(targetPath, zipFile);
    }

}
EditText中显示隐藏Android输入法窗口
在使用EditText时,会自动的弹出输入法面板,这里我们提供多种方法可以不让程序默认升起IME窗口。

  1.让EditText失去焦点,使用EditText的clearFocus方法

  2. 强制隐藏Android输入法窗口,在IME类中我们通过
  InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 实例化输入
法控制对象,通过hideSoftInputFromWindow来控制,其中第一个参数绑定的为需要隐藏输入法的EditText对象,比如 imm.hideSoftInputFromWindow(etAndroid123.getWindowToken(), 0);

在Android系统上,切换到有输入焦点的控件时会自动弹出键盘,如TextView,TextField等。
而那些没有输入焦点的控件则不会,所以没法输入。
 s
本文将以ImageView为例,介绍如何在没有输入焦点的控件上弹出键盘,并且截获输入的字符。
 
1)弹出键盘
增加一个按钮,或者在控件的点击事件中加入以下代码:
InputMethodManager input =(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);//得到InputMethodManager。

input.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);

2)扩展BaseInputConnection类,并通过ImageView的onCreateInputConnection()接口进行加载:
publicclass XXXXInputConnection extends BaseInputConnection{
  publicboolean commitText(CharSequencetext,int newCursorPosition){      //提交字符到需要输入的控件,可在此次监
听输入的字符值
  }}

 
public InputConnection onCreateInputConnection(EditorInfo outAttrs){
/*在此处可设置outAttrs定制键盘的类型*/        returnnew XXXXInputConnection(this, false);    }

3) 为了让系统能调用到ImageView的onCreateInputConnection()接口,必须把ImageView对象的属性设置如下:
setFocusable(true);
setFocusableInTouchMode(true);

 
-----------------------------------
Android没有输入焦点类控件的输入法调用
android如何调用显示和隐藏系统默认的输入法

http://soft-dev.iteye.com/blog/1073676
android如何调用显示和隐藏系统默认的输入法

android输入法 2010-11-23 21:36:47 阅读835 评论2   字号:大中小 订阅
1.调用显示系统默认的输入法

方法一、

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

imm.showSoftInput(m_receiverView(接受软键盘输入的视图(View)),InputMethodManager.SHOW_FORCED(提供当前操作的标
记,SHOW_FORCED表示强制显示));

方法二、

InputMethodManager m=(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
m.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); (这个方法可以实现输入法在窗口上切换显示,如果输入法在窗
口上已经显示,则隐藏,如果隐藏,则显示输入法到窗口上)

2.调用隐藏系统默认的输入法

((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(WidgetSearchActivity.
this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);  (WidgetSearchActivity是当前
的Activity)

3.获取输入法打开的状态

InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
boolean isOpen=imm.isActive();
isOpen若返回true,则表示输入法打开 
将磁盘转成ntfs
开始→程序→附件→命令提示符中输入convert c: /fs:ntfs即可将你的C盘无损转换成NTFS
自定义ViewGroup实现拖动 跟快速滚动的效果
自定义的viewGroup 实现以下的效果:
1:跟着手指 移动(上下移动)
2:快速推一下 滑动过去(上下滑动)
我的 代码 两个效果分开来 没有问题 放一起就出错了。。。大侠帮忙看下  上代码:
public class Workspace extends ViewGroup implements  OnGestureListener {

private GestureDetector detector;  
int move=0;//移动距离
int MAXMOVE=1030;//最大移动距离
private Context Mcontext;
AllShelf mAllShelf;
private Scroller mScroller;
public Workspace(Context context) {
  super(context);
  Mcontext=context;
//加入 孩子viewGroup
   mScroller = new Scroller(context);
    mAllShelf=new AllShelf(context)
    {
              @Override
              public void computeScroll(){
             if (mScroller.computeScrollOffset()) {     
              //返回当前滚动X方向的偏移
             // Log.d("lay1_computeScroll","mScroller.getCurrX()  "+mScroller.getCurrX());
                 scrollTo(0, mScroller.getCurrY());
                 postInvalidate();
             }
         }
     };
     
   this.addView(mAllShelf);

   detector = new GestureDetector(this);      
}

  @Override  
     public boolean onTouchEvent(MotionEvent ev) {  
     
         final int action = ev.getAction();
         
          final float y = ev.getY();
         switch (ev.getAction())
         
         {
         case MotionEvent.ACTION_DOWN:
                    break;
         case MotionEvent.ACTION_MOVE:     
             break;
         case MotionEvent.ACTION_UP:
                   break;
         }     
         return this.detector.onTouchEvent(ev);  
     }  
  @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
  // TODO Auto-generated method stub
          int childTop=0;   
         final int count = getChildCount();   
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             if (child.getVisibility() != View.GONE) {
              child.setVisibility(View.VISIBLE);   
                    child.measure(r - l, b - t);
                 final int childHeight = child.getMeasuredHeight();
                 child.layout(0, childTop, 320,childTop+ 1460);
                 childTop += 1460;
             }
         }
}

public boolean onDown(MotionEvent e) {
  // TODO Auto-generated method stub
  Log.d("onDown","onDown");
  return true;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
   float velocityY) {
  if(mScroller.isFinished())
  {
  Log.d("onFling","onFling");
  // TODO Auto-generated method stub
  //往上是负数    正数往下
  Log.d("onFling","onFling");
  
  int movethis=-(int)(velocityY*0.2);
  //int movethis;
  if(-velocityY>0)
  {
    movethis=Math.min(MAXMOVE-move, movethis);
  }else
  {
   
    movethis=Math.max(-move,movethis);
  }
  
  Log.d("movethis,,,,,velocityY,,,,,,","  "+movethis+"  "+velocityY+"  "+move);
  
  mScroller.startScroll(0, move, 0, movethis, 3000);
   
  //Log.d("movethis", ""+movethis);
  
  move=move+movethis;
  
  Log.d("move", ""+move);
  
  //mScroller.fling(0,mScroller.getFinalY (),0,-(int)velocityY,0,0,0,MAXMOVE);
   
        mAllShelf.computeScroll();
         
  }
  return false;
}

boolean IsScrollBeforeFling=false;
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
   float distanceY) {
  if(mScroller.isFinished())
  {
  Log.d("onScroll", "onScroll");      
      final int deltaY = (int) (distanceY);
         
        // mLastMotionY=y;
   
         if (deltaY < 0) {
         
          //下移
            //Log.d("down","  "+getScrollY());
             if (move > 0) {
             int move_this=Math.max(-move, deltaY);
             move=move+move_this;
            Log.d("move down", ""+move);         
                 scrollTo(0, move);
              }
            
         } else if (deltaY > 0) {                 
          //上移
            
             if (MAXMOVE-move > 0) {   
              int move_this=Math.min(MAXMOVE-move, deltaY);
              
              move=move+move_this;   
              Log.d("move up", ""+move);         
              scrollTo(0, move);                    
             }
         }
  }
  return false;
}
public void onShowPress(MotionEvent e) {
}
public boolean onSingleTapUp(MotionEvent e) {
   return false;
}
public void onLongPress(MotionEvent e) {
}
}
快速滑动ListView有一个属性可以设置
android:fastScrollEnabled="true"
类似于系统联系人一样

问题解决了。。。。onScroll 里的  scrollTo 竟然没写明对象。。。 mAllShelf.scrollTo 就OK了 分给 唯一关注的大哥
android 软件盘弹出时的界面控制
一、软键盘显示的原理
软件盘的本质是什么?软键盘其实是一个Dialog!
InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参数(如Gravity)进行了设置,使之能够在底部或者全屏显示。当我们点击输入框时,系统对活动主窗口进行调整,从而为输入法腾出相应的空间,然后将该Dialog显示在底部,或者全屏显示。

二、活动主窗口调整
android定义了一个属性,名字为windowSoftInputMode, 用它可以让程序可以控制活动主窗口调整的方式。我们可以在AndroidManifet.xml中对Activity进行设置。如:android:windowSoftInputMode="stateUnchanged|adjustPan"
模式一,压缩模式
windowSoftInputMode的值如果设置为adjustResize,那么该Activity主窗口总是被调整大小以便留出软键盘的空间。
我们通过一段代码来测试一下,当我们设置了该属性后,弹出输入法时,系统做了什么。
重写Layout布局:

   1. public class ResizeLayout extends LinearLayout{
   2. private static int count = 0;
   3. public ResizeLayout(Context context, AttributeSet attrs) {
   4. super(context, attrs);
   5. }
   6. @Override
   7. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   8. super.onSizeChanged(w, h, oldw, oldh);
   9. Log.e("onSizeChanged " + count++, "=>onResize called! w="+w + ",h="+h+",oldw="+oldw+",oldh="+oldh);
  10. }
  11. @Override
  12. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  13. super.onLayout(changed, l, t, r, b);
  14. Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b="+b);
  15. }
  16. @Override
  17. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  18. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  19. Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec);
  20. }

我们的布局设置为:

   1. <com.winuxxan.inputMethodTest.ResizeLayout
   2. xmlns:android="http://schemas.android.com/apk/res/android"
   3. android:id="@+id/root_layout"
   4. android:layout_width="fill_parent"
   5. android:layout_height="fill_parent"
   6. android:orientation="vertical"
   7. >
   8. <EditText
   9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. />
  12. <LinearLayout
  13. android:id="@+id/bottom_layout"
  14. android:layout_width="fill_parent"
  15. android:layout_height="fill_parent"
  16. android:orientation="vertical"
  17. android:gravity="bottom">s
  18. <TextView
  19. android:layout_width="fill_parent"
  20. android:layout_height="wrap_content"
  21. android:text="@string/hello"
  22. android:background="#77777777"
  23. />
  24. </LinearLayout>
  25. </com.winuxxan.inputMethodTest.ResizeLayout>

AndroidManifest.xml的Activity设置属性:android:windowSoftInputMode = "adjustResize"
运行程序,点击文本框,查看调试信息:
E/onMeasure 6(7960): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec = 1073742024
E/onMeasure 7(7960): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec = 1073742025
E/onSizeChanged 8(7960): =>onSizeChanged called! w=320,h=201,oldw=320,oldh=377
E/onLayout 9(7960): =>OnLayout called! l=0, t=0,r=320,b=201
从调试结果我们可以看出,当我们点击文本框后,根布局调用了onMeasure,onSizeChanged和onLayout。
实际上,当设置为adjustResize后,软键盘弹出时,要对主窗口布局重新进行measure和layout,而在layout时,发现窗口的大小发生的变化,因此调用了onSizeChanged。
从下图的运行结果我们也可以看出,原本在下方的TextView被顶到了输入法的上方

模式二,平移模式
windowSoftInputMode的值如果设置为adjustPan,那么该Activity主窗口并不调整屏幕的大小以便留出软键盘的空间。相反,当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分。这个通常是不期望比调整大小,因为用户可能关闭软键盘以便获得与被覆盖内容的交互操作。
上面的例子中,我们将AndroidManifest.xml的属性进行更改:android: windowSoftInputMode = "adjustPan"

重新运行,并点击文本框,查看调试信息:
E/onMeasure 6(8378): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec=1073742200
E/onMeasure 7(8378): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec=1073742201
E/onLayout 8(8378): =>OnLayout called! l=0, t=0,r=320,b=377
我们看到:系统也重新进行了measrue和layout,但是我们发现,layout过程中onSizeChanged并没有调用,这说明输入法弹出前后并没有改变原有布局的大小。
从下图的运行结果我们可以看到,下方的TextView并没有被顶到输入法上方。

事实上,当输入框不会被遮挡时,该模式没有对布局进行调整,然而当输入框将要被遮挡时,窗口就会进行平移。也就是说,该模式始终是保持输入框为可见。如下图,整个
Android如何获得系统(system)权限
Android中如何修改系统时间(应用程序获得系统权限)

  在 android 的API中有提供 SystemClock.setCurrentTimeMillis()函数来修改系统时间,可惜无论你怎么调用这个函数都是没用的,无论模拟器还是真机,在logcat中总会得到"Unable to open alarm driver: Permission denied ".这个函数需要root权限或者运行与系统进程中才可以用。

  本来以为就没有办法在应用程序这一层改系统时间了,后来在网上搜了好久,知道这个目的还是可以达到的。

  第一个方法简单点,不过需要在Android系统源码的环境下用make来编译:

  1. 在应用程序的AndroidManifest.xml中的manifest节点中加入

  android:sharedUserId="android.uid.system"这个属性。

  2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform这一行

  3. 使用mm命令来编译,生成的apk就有修改系统时间的权限了。



  第二个办法麻烦点,不过不用开虚拟机跑到源码环境下用make来编译:

  1. 同上,加入android:sharedUserId="android.uid.system"这个属性。

  2. 使用eclipse编译出apk文件,但是这个apk文件是不能用的。

  3. 用压缩软件打开apk文件,删掉META-INF目录下的CERT.SF和CERT.RSA两个文件。

  4. 使用目标系统的platform密钥来重新给apk文件签名。这步比较麻烦,

  首先找到密钥文件,在我的Android源码目录中的位置

  是"build        argetproductsecurity",下面的platform.pk8和platform.x509.pem

  两个文件。

  然后用Android提供的Signapk工具来签名,signapk的源代码是

  在"build        oolssignapk"下,

  用法为"signapk platform.x509.pem platform.pk8 input.apk output.apk",

  文件名最好使用绝对路径防止找不到,也可以修改源代码直接使用。

  这样最后得到的apk和第一个方法是一样的。

  最后解释一下原理,首先加入android:sharedUserId="android.uid.system"这个属性。通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中。那么把程序的UID配成android.uid.system,也就是要让程序运行在系统进程中,这样就有权限来修改系统时间了。

  只是加入UID还不够,如果这时候安装APK的话发现无法安装,提示签名不符,原因是程序想要运行在系统进程中还要有目标系统的platform

key,就是上面第二个方法提到的platform.pk8和platform.x509.pem两个文件。用这两个key签名后apk才真正可以放入系统进程中。第一个方法中加入LOCAL_CERTIFICATE := platform其实就是用这两个key来签名。  这也有一个问题,就是这样生成的程序只有在原始的Android系统或者是自己编译的系统中才可以用,因为这样的系统才可以拿到 platform.pk8和platform.x509.pem两个文件。要是别家公司做的Android上连安装都安装不了。试试原始的Android 中的key来签名,程序在模拟器上运行OK,不过放到G3上安装直接提示"Package ... has no signatures that match those in shared user android.uid.system",这样也是保护了系统的安全。  最最后还说下,这个android:sharedUserId属性不只可以把apk放到系统进程中,也可以配置多个APK运行在一个进程中,这样可以共享数据,应该会很有用的。
Android 使用 ToneGenerator 编写按键发声功能
private ToneGenerator mToneGenerator;

  private Object mToneGeneratorLock = new Object();//监视器对象锁

  private boolean mDTMFToneEnabled; //按键操作音

  private static final int TONE_LENGTH_MS = 150;//延迟时间

  void playTone(int tone) {

  // TODO 播放按键声音

  if (!mDTMFToneEnabled) {

  return;

  }

  AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

  int ringerMode = audioManager.getRingerMode();

  if ((ringerMode == AudioManager.RINGER_MODE_SILENT)

  || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {//静音或震动时不发出按键声音

  return;

  }

  synchronized(mToneGeneratorLock) {

  if (mToneGenerator == null) {

  Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);

  return;

  }

  mToneGenerator.startTone(tone, TONE_LENGTH_MS);//发声

  }

  }

  protected void onResume(){

  super.onResume();

  mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),

  Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;//获取系统参数“按键操作音”是否开启

  synchronized(mToneGeneratorLock) {

  if (mToneGenerator == null) {

  try {

  mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80);

  setVolumeControlStream(AudioManager.STREAM_MUSIC);

  } catch (RuntimeException e) {

  Log.w(TAG, "Exception caught while creating local tone generator: " + e);

  mToneGenerator = null;

  }

  }

  }

  }

android中自定义标题栏
http://www.itivy.com/android/archive/2011/8/20/android-activity-title-bar-operation.html
Android 2.2中的APK安装参数installLocation
在Android 2.2中新的特性可以支持类似APP2SD卡上,我们的APK文件可以安装在SD卡上供用户使用,Android123今天就说下目前项目的升级和一些配置。
  1. 首先让你的程序支持SD卡上安装必须具备设置API Level至少为8,即androidmanifest.xml的中android:minSdkVersion至少为8这样你的APK最终运行时兼容的固件只有2.2了,同时在androidmanifest.xml文件的根节点中必须加入android:installLocation这个属性,类似代码如下:
  < manifest xmlns:andro
  android:installLocation="preferExternal"
  ... >
  复制代码
  2. android:installLocation的值主要有preferExternal、auto 和internalOnly这三个选项,通常我们设置为preferExternal可以优先推荐应用安装到SD卡上,当然最终用户可以选择为内部的 ROM存储上,如果外部存储已满,Android内部也会安装到内部存储上,auto将会根据存储空间自适应,当然还有一些应用可能会有特殊的目的,他们一般必须安装在内部存储才能可靠运行,设置为internalOnly比较合适,主要体现在:
  Services 服务
  Your running Service will be  kill ed and will not be restarted when external storage is remounted. You can, however, register for the ACTION_EXTERNAL_APPLICATIONS_AVAILABLE broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service. Android123提示大家一般实时后台监控类的应用都应该装到内部存储,比较可靠。
  Alarm Services 闹铃提醒服务
  Your alarms registered with AlarmManager will be cancelled. You must manually re-register any alarms when external storage is remounted.
  Input Method Engines 输入法引擎
  Your IME will be replaced by the default IME. When external storage is remounted, the user can open system settings to enable your IME again.
  Live Wallpapers 活动壁纸
  Your running Live Wallpaper will be replaced by the default Live Wallpaper. When external storage is remounted, the user can select your Live Wallpaper  
〖黑软手机 资讯频道〗
  
again.  
  Live Folders 活动文件夹
  Your Live Folder will be removed from the home screen. When external storage is remounted, the user can add your Live Folder to the home screen again.
  App Widgets Widget
  Your App Widget will be removed from the home screen. When external storage is remounted, your App Widget will not be available for the user to select until the system resets the home application (usually not until a system reboot).
  Account Managers 账户管理
  Your accounts created with AccountManager will disappear until external storage is remounted.
  Sync Adapters 同步适配器
  Your AbstractThreadedSyncAdapter and all  it s sync functionality will not work until external storage is remounted.
  Device Administrators 设备管理器
  Your DeviceAdminReceiver and all  it s admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
  那么哪些应用适合安装在SD卡中呢? Android开发网建议一些占用资源比较大的游戏,比如大于3MB的单个文件,不需要长期驻留内存的应用,不具备提醒和实时监控的应用一般放到SD卡上比较合适,不过目前想让你的应用装到SD卡上,必须设置API Level至少为8以上,同时显示注明android:installLocation。
Android 使用 ToneGenerator 编写按键发声功能
private ToneGenerator mToneGenerator;

  private Object mToneGeneratorLock = new Object();//监视器对象锁

  private boolean mDTMFToneEnabled; //按键操作音

  private static final int TONE_LENGTH_MS = 150;//延迟时间

  void playTone(int tone) {

  // TODO 播放按键声音

  if (!mDTMFToneEnabled) {

  return;

  }

  AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

  int ringerMode = audioManager.getRingerMode();

  if ((ringerMode == AudioManager.RINGER_MODE_SILENT)

  || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {//静音或震动时不发出按键声音

  return;

  }

  synchronized(mToneGeneratorLock) {

  if (mToneGenerator == null) {

  Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);

  return;

  }

  mToneGenerator.startTone(tone, TONE_LENGTH_MS);//发声

  }

  }

  protected void onResume(){

  super.onResume();

  mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),

  Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;//获取系统参数“按键操作音”是否开启

  synchronized(mToneGeneratorLock) {

  if (mToneGenerator == null) {

  try {

  mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80);

  setVolumeControlStream(AudioManager.STREAM_MUSIC);

  } catch (RuntimeException e) {

  Log.w(TAG, "Exception caught while creating local tone generator: " + e);

  mToneGenerator = null;

  }

  }

  }

  }

ubuntu/linux下查看端口使用情况
想查看TCP或者UDP端口使用情况,使用
netstat -anp
如果有些进程看不见,如只显示”-”,可以尝试
sudo netstat -anp
如果想看某个端口的信息,使用lsof命令,如:
sudo lsof -i :631

-bash-3.00# netstat -tln

netstat -tln 命令是用来查看linux的端口使用情况

/etc/init.d/vsftp start 是用来启动ftp端口~!

看文件/etc/services

netstat

查看已经连接的服务端口(ESTABLISHED)

netstat -a

查看所有的服务端口(LISTEN,ESTABLISHED)

sudo netstat -ap

查看所有 的服务端口并显示对应的服务程序名

nmap <扫描类型><扫描参数>

例如:

nmap localhost

nmap -p 1024-65535 localhost

nmap -PT 192.168.1.127-245

当我们使用 netstat -apn 查看网络连接的时候,linux会发现很多类似下面的内容:

Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name

tcp 0 52 218.104.81.152:7710 211.100.39.250:29488 ESTABLISHED 6111/1

显示这台服务器开放了7710端口,那么 这个端口属于哪个程序呢?我们可以使用 lsof -i :7710 命令来查询:
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME

sshd 1990 root 3u IPv4 4836 TCP *:7710 (LISTEN)

这样,我们就知道了7710端口是属于sshd程序的。
解开“OEM”与“ODM”之谜 http://tech.sina.com.cn/c/2001-09-26/6123.html
OEM一词在电脑硬件产品中简直实在是太普遍了,像OEM光驱、OEM显示器、OEM鼠标什么的。有些人甚至以为它是一个有质量保证的品牌。那么,你对OEM又有多少认识呢?说一件产品是OEM产品是否就代表信得过呢?

  其实,OEM与现代工业社会有着密切的关系。一些著名的品牌商品制造商,常常因为自己的厂房不能达到大批量生产的要求,又或者需要某些特定的零件,因此向其他厂商求助。 
这些伸出援手的厂商就被称为OEM(Original Equipment Manufacturer,原始设备生产商)。如将之引申到IT领域的话,则表示那些进行代工的生产商。例如CPU风扇,Intel或AMD公司本身并不生产,它们通常会找像日本三洋公司这样的专业电机制造企业作风扇OEM生产。

  ODM又是怎么一回事呢?原来,某制造商设计出一种产品后,在某些情况下可能会被另外一些品牌的制造商看中,要求配上后者的品牌名称来进行生产,又或者稍微修改一些设计(如按键位置)来生产。这样做的最大好处是其他厂商减少了自己研制的时间。有些人也习惯性称这些产品是OEM,实际上应该称之为ODM(Original Design Manufacturer,原始设计制造商)。例如一些日本品牌的笔记本电脑实际上就是由台湾厂商代工生产的。事后,台湾笔记本电脑制造商只要修改某些设计细节或配件便可以以自己的品牌名称进行批量生产。原因在于它们为这些日本品牌作的是ODM而非OEM。当然,我们可以说它们都是从同一条生产线生产出来。

  OEM和ODM两者最大的区别不单单是名称而已。OEM产品是为品牌厂商度身订造的,生产后也只能使用该品牌名称,绝对不能冠上生产者自己的名称再进行生产。而ODM则要看品牌企业有没有买断该产品的版权。如果没有的话,制造商有权自己组织生产,只要没有企业公司的设计识别即可。

  在工业社会中,OEM和ODM可谓司空见惯。因为出于制造成本、运输方便性、节省开发时间等方面的考虑,知名品牌企业一般都愿意找其他厂商OEM或ODM。在找别的企业进行OEM或ODM时,知名品牌企业也要承担不少责任。毕竟产品冠的是自己的牌子,如果产品质量不佳的话,少则有顾客找上门投诉,重则可能要上法庭。所以,品牌企业在委托加工期间肯定会进行严格的质量控制。但代工结束后,质量不敢保证。故此,当有的商家告诉你某件产品的生产商是某大品牌的OEM或ODM产品时,绝不要相信其质量就等同于该品牌。你唯一能够相信的,是这家制造商有一定的生产能力。

  另外,不知大家是否遇到过这样的情形:到电脑城购买产品(如LCD显示器)时,商家向你介绍某个台湾品牌的LCD显示器的液晶面板是某某日本大厂OEM的。其实这种说法是一个颇为错误的概念。首先,为保障品牌的质量和信誉,ODM或OEM公司的名称是保密的。如果说穿了A牌子笔记本电脑是B牌子生产的话,那人家还会买价格更贵的A牌子产品吗?因此上述LCD显示器的例子,实际上只是该台湾品牌购买了日本品牌的液晶面板配件来进行生产。质量好或不好,并不能完全相信。那我们该如何识别是否代工厂商采用了知名厂商的配件呢?以LCD显示器为例,目前其核心技术仍然控制在日本和韩国几家大厂商手中。消费者可以从技术指标来进行鉴别。如对比度为400:1的液晶显示器只有日本的日立(Hitachi)和富士通(Fujitsu)等公司生产,而对比度为350:1的产品则主要由韩国三星公司和日本NEC公司生产。

  其实对于消费者来说,最怕听到的就是一些似是而非的简称,什么OEM、ODM之类,有时真的听得人一头雾水。不过,只要我们多看多学一些新知识,遇到有销售商家说OEM或ODM的问题,不要怕烦,先回家查清楚,那无论是OEM或ODM,问题都不大了。

Linux下rpm 安装包方式安装
为了方便linux 用户添加和删除软件,Red Hat 公司提出了软件包管理器RPM,由于它的出现使得在linux 中安装、卸载应用程序变得相对简单,默认情况下(即不出现文件依赖问题)用户只需双击rpm 软件包,系统会自动进行安装。

  一个rpm包文件是能够让应用软件运行的全部文件的一个集合,它记录了二进制软件的内容、安装的位置、软件包的描述信息、软件包之间的依赖关系等信息。RPM 工具对系统中全部rpm 软件包进行全面管理,因此它能够记住用户添加了什么以及这些软件每个文件的具体安装路径,以便用户完全地、彻底地删除。一般来说,rpm 软件包发布的软件比需要手工编译的软件容易安装和维护,但是有些rpm软件包需要大量的依赖包,这时如果没有联网也是比较头痛的一件事情。

  下面介绍命令行方式安装rpm,在终端中我们可以使用rpm -i [选项] [rpm 包文件名]来进行安装,常用选项如下:

  -h 使用符号#显示安装进度
  -v 报告每一步操作的情况
  --replacepkge 无论软件包是否已被安装,都强行安装软件包
  --test 安装测试,并不实际安装
  --nodeps 忽略软件包的依赖关系强行安装(一般不能正常运行,因为缺少依赖文件)
  --force 忽略软件包及文件的冲突

  假设在root 用户桌面上有一个notepad.rpm 软件包,那么我们可以在终端中输入:

  rpm -ivh /root/Desktop/notepad.rpm进行安装,如果出现了错误提示“error: Faild dependencies”则表明出现了软件包依赖问题,下面会有提示需要哪个文件,安装完
哪个文件后(google 一个)再次安装即可。也可以在上述命
令的后面加上--nodeps 强制安装。

  如果需要删除rpm 软件包可以使用下面的命令

  rpm -e notepad

  如果同样出现依赖问题同样加上--nodeps参数。如果大家的系统能够连接互联网,则在图形界面下双击rpm 软件包即使出现了依赖问题,软件包会自动到网络下载相应依赖文件后继续安装。 
Android通话状态(PhoneState)的获取
1. 利用繼承PhoneStateListener來實作當通話狀態為閒置、接起或響起時我們所要做的動作。
2. 由於我們需取得目前手機的通話狀態,因此必需在AndroidManifest.xml內新增一個讀取通話狀態的權限。
3. MainActivity.java

package org.me.android_callstate;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        //電話狀態的Listener
        MyPhoneStateListener myPhoneStateListener = new MyPhoneStateListener();
        //取得TelephonyManager
        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        //將電話狀態的Listener加到取得TelephonyManager
        telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    public class MyPhoneStateListener extends PhoneStateListener {
        @Override
        public void onCallStateChanged(int state, String phoneNumber) {
            switch (state) {
                //電話狀態是閒置的
                case TelephonyManager.CALL_STATE_IDLE:
                    break;
                //電話狀態是接起的
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    Toast.makeText(MainActivity.this, "正接起電話…", Toast.LENGTH_LONG).show();
                    break;
                //電話狀態是響起的
                case TelephonyManager.CALL_STATE_RINGING:
                    Toast.makeText(MainActivity.this, phoneNumber + "正打電話來…", Toast.LENGTH_LONG).show();
                    break;
                default:
                    break;
            }
        }
    }
}

4.AndroidManifest.xml

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="org.me.android_callstate">
    <application>
         <activity android:name=".MainActivity" android:label="MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
</manifest>
setVolumeControlStream(int streamType)

当开发多媒体应用或者游戏应用的时候,需要使用音量控制键来设置程序的音量大小。在Android系统中有多中音频流,通过Activity中的函数 setVolumeControlStream(int streamType)可以设置该Activity中音量控制键控制的音频流,一般在onCreate函数中设置。

Android中有如下几种音频流:

    * AudioManager.STREAM_MUSIC  /** The audio stream for music playback */
    * AudioManager.STREAM_RING /** The audio stream for the phone ring */
    * AudioManager.STREAM_ALARM  /** The audio stream for alarms */
    * AudioManager.STREAM_NOTIFICATION /** The audio stream for notifications */
    * AudioManager.STREAM_SYSTEM  /** The audio stream for system sounds */
    * AudioManager.STREAM_VOICECALL /** The audio stream for phone calls */

setVolumeControlStream函数描述:
void android.app.Activity .setVolumeControlStream(int streamType)

Suggests an audio stream whose volume should be changed by the hardware volume controls.

The suggested audio stream will be tied to the window of this Activity. If the Activity is switched, the stream set here is no longer the suggested stream. The client does not need to save and restore the old suggested stream value in onPause and onResume.

Parameters:
    streamType The type of the audio stream whose volume should be changed by the hardware volume controls. It is not guaranteed that the hardware volume controls will always change this stream's volume (for example, if a call is in progress, its stream's volume may be changed instead). To reset back to the default, use AudioManager.USE_DEFAULT_STREAM_TYPE .

android文档中allowTaskReparenting疑问
If an activity has its allowTaskReparenting attribute set to "true", 
it can move from the task it starts in to the task it has an affinity 
for when that task comes to the fore. For example, suppose that an 
activity that reports weather conditions in selected cities is defined
 as part of a travel application. It has the same affinity as other 
activities in the same application (the default affinity) and it allows
 reparenting. One of your activities starts the weather reporter, so it
 initially belongs to the same task as your activity. However, when the
 travel application next comes forward, the weather reporter will be 
reassigned to and displayed with that task.

一个activity1原来属于task1,但是如果task2启动起来的话,activity1可能不再属于task1了
,转而投奔task2去了。
当然前提条件是allowTaskReparenting,还有affinity设置

有点像,你捡到一条狗,在家里喂养几天觉得不错,当自己家的了;但是突然有一天他的主人找上门来了
,小狗还是乖乖和主人走了。


用法<application android:allowTaskReparenting="true/false"></application>

是否允许activity更换从属的任务,比如从短信息任务 切换到浏览器任务。

用来标记Activity能否从启动的Task移动到有着affinity的Task(当这个Task进入到前台时)——
“true”,表示能移动,“false”,表示它必须呆在启动时呆在的那个Task里。

        如果这个特性没有被设定,设定到<application>元素上的allowTaskReparenting特性
的值会应用到Activity上。默认值为“false”。

       一般来说,当Activity启动后,它就与启动它的Task关联,并且在那里耗尽它的整个生命周期
。当当前的Task不再显示时,你可以使用这个特性来强制Activity移动到有着affinity的Task中。
典型用法是:把一个应用程序的Activity移到另一个应用程序的主Task中。

       例如,如果e-mail中包含一个web页的链接,点击它就会启动一个Activity来显示这个页面
。这个Activity是由Browser应用程序定义的,但是,现在它作为e-mail Task的一部分。如果它重
新宿主到Browser Task里,当Browser下一次进入到前台时,它就能被看见,并且,当e-mail Task
再次进入前台时,就看不到它了。

       Actvity的affinity是由taskAffinity特性定义的。Task的affinity是通过读取根
Activity的affinity 决定。因此,根据定义,根Activity总是位于相同affinity的Task里。
由于启动模式为“singleTask”和 “singleInstance”的Activity只能位于Task的底部,因此,
重新宿主只能限于“standard”和“singleTop”模式。
Android 修改Menu背景
public void setMenuBackground(Context context, Menu menu) {   
        ((Activity) context).getLayoutInflater().setFactory(new Factory() {   
            @Override  
            public View onCreateView(String name, Context context,   
                    AttributeSet attrs) {   
                if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) {   
                    try {   
                        LayoutInflater f = ((Activity) context)   
                                .getLayoutInflater();   
                        final View view = f.createView(name, null, attrs);// 尝试创建我们自己布局   
                        new Handler().post(new Runnable() {   
                            public void run() {   
                                view.setBackgroundResource(R.drawable.menu_bg);// 设置背景为我们自定义的图片,替换cwj_bg文件即可   
                            }   
                        });   
                        return view;   
                    } catch (Exception e) {   
                    }   
                }   
                return null;   
            }   
        });   
    }  
手机常用查询
1   输入*#06#(手机型号,请更换):显示IMEI码  

2   输入*#0000#:显示软件版本   (部分型号如果不起作用,可按*#型号代码#,如*#6110#)  

第一行--软件版本;  

第二行--软件发布日期;  

第三行--手机型号  

3   输入*#92702689#查询更多的手机信息。有五个选项(可用上下方向键选择):  

①Serial   No.:手机的IMEI码。  

②Made:手机的制造日期.   (本人用的是诺基亚6108,   上面写的是092003,   即为2003年09月生产的   )  

③Purchasing   Date:购买日期,此日期一经设定不可更改,新机子应该是mmyyyy  

④Repaired:维修次数的记录。新机子应该是mmyyyy  

⑥Life   timer:新机子是6553501。  

B   高级指令   (专门为各位大侠准备的,   ^_^   建议一般初学者不要随便输入,以免输入错误导致手机无法复原!)  

1   速率编码  

*3370#或*efr#:开启(EFR)全速率编码  

#3370#或#efr#:关闭全速率编码(开启全速增强型编码模式,可改善话音质量但会耗电)  

*4720#或*hra0#:开启(HR)半速率编码  

#4720#或#hra0#:关闭半速率编码   (话音质量降低,但可延长电池大概30%左右使用时间,需网络支持)  

键入这些代码后,会关机重开,然后才能生效。  

2   SIM卡锁信息  

*#746025625#   (=   *#sim0clock#):如果SIM卡能被锁,进行检测时键入该指令后,手机显   示 "SIM   CLOCK   STOP   ALLOWED "   or "SIM   CLOCK   STOP   NOT   ALLOWED ",这取决于你的SIM卡。  

SIM卡锁信息:包括四种不同的锁  

1).国家锁--锁指定的国家  

2).网络锁--锁指定的网络  

3).供应商锁--锁服务提供商  

4).SIM卡锁--锁指定的SIM卡  


3   查询手机是否锁频  

首先必须找出设定手机时必须使用的几个键。其中,连续按*键二次即出现 "+ ";连续按*键三次   即出现 "p ";连续按*键四次即出现 "w "。然后,你就可以依次顺序输入相应组合键。  

#pw+1234567890+1#:查询是否锁国家码  

#pw+1234567890+2#:查询是否锁网络码  

#pw+1234567890+3#:查询是否锁网络提供者锁定的码  

#pw+1234567890+4#:查询是否锁SIM卡  

4   电源按键  

轻触电源键,屏幕将显示情景模式的选单,你可以通过上下键快速地在各个模式中切换。在键盘   锁模式下打开屏幕灯,你曾感受过在夜晚摸黑想打开手机的键盘锁的烦恼吗?其实你可以轻触电   源键,这时屏幕和键盘的夜灯将会打开,这样你就可以从容地分辨按键打开键盘锁了。  

5   查看手机状态  

操作指令:#pw+(mastercode)+X#(mastercode)是一10位数(没有括号)   X是一个1到4的数,   它显示以上的锁,还不确定何数对应   何锁。  

SIM卡不限制信息,意味着你的电话还没锁。  

1).VIN   CHARGER   INPUT   VOLTAGE   8.4V   0.8A  

2).CHRG   CTRL   CHARGER   CONTROL   PWM   32Khz  

3).XMIC   MIC   INPUT   60mV   -   1V  

4).SGND   SIGNAL   GROUND  

5).XEAR   EAR   OUTPUT   80mV   -   1V  

6).MBUS   9600   B/S  

7).FBUS_RX   9.6   -   230.4   KB/S  

8).FBUS_TX   9.6   -   230.4   KB/S  

9).L_GND   CHARGER   /   LOGIC   GND  

C   诺基亚其他手机指令内容  

1   NOKIA   9000/9000i  

1).显示IMEI号:*#06#  

2).显示软件版本号:*#682371158412125#   最新版本在电话信息下面  

3).显示制造星期和年:   *#3283#  

4).电话类型:   GE8  


2   5110锁码机解码  

操作步骤:按C,按下   ->   按C不放   ->   按*不放   -> 按*不放   -> 键入04*PIN*PIN*PIN#,就完成了。  

3   更换当前号码  

按住#键不放约一秒钟,屏幕会出现“交换号码?”   ,按确认后屏幕上方会出现一个2字,这时   手机不能拨出但能接听来电。取消重操作一次,利用它可达到锁机的效果。在非来电时进入来电   菜单,持续按住功能键两秒,将进入来电菜单,而这个菜单平时只是会在有电话打入时才会出现   的。  

4   6110在待机画面显示名字  

选择一个最不常使用的操作模式,通常是寻呼机   Menu3-5,将个人化选择Menu3-5-2设定完后,   重新命名Menu3-5-3为自己的名字,启动后即可。备注:若要恢复为寻呼机,重新命名Menu3-5   -3为空白即可。  

D   查验“手机串号”:www.chinamobile.gov.cn;  

辨别“进网许可证”真伪:www.tenaa.com.cn!  

如果买的是原装水货,一般来说质量还是行的;如果是改版就难说了,不法商家私自将英文机改装成中文  
机,将英文机软件置换,使之可以输入中文。此种手机在使用中可能会出现字符乱码、死机等现象。  

E   还有一个鲜为人知的:  

恢复GMS蜂窝网运营商的工程代码:*#67705646#  
如果是“全球通”或“神州行”的SIM卡就将待机画面恢复为“中国电信”或“中国移动”;如果是中国联通用户,就显示为“中国联通”! 
Android获取手机和系统版本等信息的代码
String phoneInfo = "Product: " + android.os.Build.PRODUCT;
        phoneInfo += ", CPU_ABI: " + android.os.Build.CPU_ABI;
        phoneInfo += ", TAGS: " + android.os.Build.TAGS;
        phoneInfo += ", VERSION_CODES.BASE: " + android.os.Build.VERSION_CODES.BASE;
        phoneInfo += ", MODEL: " + android.os.Build.MODEL;
        phoneInfo += ", SDK: " + android.os.Build.VERSION.SDK;
        phoneInfo += ", VERSION.RELEASE: " + android.os.Build.VERSION.RELEASE;
        phoneInfo += ", DEVICE: " + android.os.Build.DEVICE;
        phoneInfo += ", DISPLAY: " + android.os.Build.DISPLAY;
        phoneInfo += ", BRAND: " + android.os.Build.BRAND;
        phoneInfo += ", BOARD: " + android.os.Build.BOARD;
        phoneInfo += ", FINGERPRINT: " + android.os.Build.FINGERPRINT;
        phoneInfo += ", ID: " + android.os.Build.ID;
        phoneInfo += ", MANUFACTURER: " + android.os.Build.MANUFACTURER;
        phoneInfo += ", USER: " + android.os.Build.USER;
        // Toast.makeText(this, phoneInfo, Toast.LENGTH_LONG).show();
        TextView t = (TextView) findViewById(R.id.main_phoneinfo);
        t.setText(phoneInfo);
改变android程序head样式
我们怎么把自己的程序头部弄好看一点呢? 一步一步来 
第一编写头部布局xml文件 
<LinearLayout 
android:id="@+id/activity_title" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
android:orientation="horizontal" 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:gravity="center_vertical" 
  > 
<ImageView 
android:id="@+id/icon" 
android:layout_width="25px" 
android:layout_height="25px" 
android:background="@drawable/icon" 
> 
</ImageView> 
<TextView 
android:id="@+id/app_title" 
android:layout_width="wrap_content" 
android:layout_height="25px" 
android:text="@string/app_name" 
android:paddingLeft="8px" 
android:paddingTop="2px" 
> 
</TextView> 
</LinearLayout> 
第二在每个activity中 
super.onCreate(savedInstanceState); 
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); 
setContentView(R.layout.main); 
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,R.layout.title); 
注意这四句代码的顺序 
这样的话头部就变得有图标有文字,而且有背景了,但是有一个问题,就头部两边总有空白的地方,很是难看,现在在做地三步 
在样式文件中添加<style name="CustomWindowTitleBackground"> 
       <item name="android:background">@drawable/title_bg</item> 
       <item name="android:textSize">17dp</item> 
       <item name="android:textColor">#ffffff</item> 
       <item name="android:paddingLeft">14dp</item> 
</style> 


<style name="test" parent="android:Theme"> 
     <item name="android:windowTitleSize">40dp</item> 
     <item name="android:windowTitleBackgroundStyle">@style/CustomWindowTitleBackground</item> 
</style> 
第四在manifest中所需要改变的activity标签中添加android:theme ="@style/test" 
这样就解决了第二步所存在的问题了 
关于android应用程序使用ActivityManager退出的问题!
1.5-2.1之前都是

ActivityManager activityManager =
            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        activityManager.restartPackage("包名");

就搞定了。

但2.2就不行,只是把后台数据清空了,没有关闭页面,导致null异常。

解决方案:

步骤1:
/**
     * activityList:所有activity对象,用于退出时全部finish; Activity走onCreate时,添加到该集合
     */
    public static List<Activity> activityList = new ArrayList<Activity>();

步骤2:
/**
     * 页面初始化
     * 
     * @param savedInstanceState
     */
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

       activityList.add(this);

步骤3:
/**
     * 退出客户端。
     * 
     * @param context 上下文
     */
    public static void exitClient(Context context)
    {
        Log.d(TAG, "----- exitClient -----");
        // 关闭所有Activity
        for (int i = 0; i < activityList.size(); i++)
        {
            if (null != activityList.get(i))
            {
                activityList.get(i).finish();
            }
        }
        ActivityManager activityManager =
            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        activityManager.restartPackage("com.huawei.softclient.mtvclient");
        System.exit(0);
    }


这里要加System.exit(0);
<uses-permission andriod:name="android.permission.RESTART_PAKCEAGES">

该方法经过专业测试已经通过。

当时搞这东西害得我头大了几天,主要是资料太少了,只能自己研究。真是研究生啊。。。
----------------------------------------------------------------------------------------------------------------------
很多网友不知道,Android应用如何完全退出,此前Android123讲过了三种比较常见的方法,参考 Android程序完全退出的三种方法
一文,但是有的网友可能发现Android 2.2中即使使用了Google最新给出的Android 2.2新增API
killBackgroundProcesses 方法也无法实现。在J2SE中我们可能使用System.exit(0); 
退出,那么我们提到的5种常规方法都无法完全退出怎么办呢?
  今天Android开发网给出大家第六种非常规方法,制造异常退出。
  1. 首先我们可以制造一个空指针的异常,比如TextView.setText方法中执行一个int型的内容,由于setText方法重载了 R.string.xxx这样的资源int型内容,但是我们没有声明这个资源,仅仅把String写成了int的值,就会产生一个异常,
android总结
1. 当发生ANR时,记录会保存在data/anr/traces.txt里 

2. 关于LBS(Location Based Service),基于位置的服务。关键是要获得坐标信息,然后就可以根据坐标来实现各种业务。

android提供了两种基本的方式来获取坐标,一种是基于GPS,一种是基于NETWORK,包括基站定位和WIFI定位。 

GPS的优点是只要有GPS传感器,就可以得到坐标,得到坐标的原理有很多,包括时间差等等。GPS定位的优势是坐标比较精确,
而且即使没有网络也是可以的(当然得到坐标以后的后续处理,没网络还是不行的)。缺点是当在隧道,或者室内的时候,
接收不到卫星信息,就无法得到坐标。android中对应GPS定位的provider是GPS_PROVIDER 

基站定位的优点是,只要在基站的覆盖范围内(换言之,手机有信号),就可以得到cell id和LAC(Location Area Code)。
然后通过将cell id和LAC发送到某个系统,这个系统就可以返回位置信息。这有个条件,就是某个系统拥有位置信息。
可能是运营商提供的系统(通常要收费),也可能是免费系统(比如常用的google)。这个方式的缺点是得到的坐标可能不会很精确,
在一个基站覆盖范围内的任何一点,得到的都是同样的坐标,坐标的精确度取决于基站小区的覆盖密度。
另外同样的cell id和LAC,发送给不同的系统,得到的坐标也有可能是不同的。 

android中对应基站定位的provider是NETWORK_PROVIDER。另外,还没看过源代码,但我猜测如果用android api来做基站定
位,那应该默认是把cell id和LAC发送到google服务器上,那么就必须要求能上网了。其实如果位置信息系统提供了短信网关接口
,也是可以不上网的,这样就不能用android api了,而是要获取到cell id和LAC(可能还要求别的参数,根据位置信息系统提供
商提供的接口决定),然后以短信方式发送到位置信息系统,也是可以的。我们这次就打算这么做 

3. 关于adapter,现在理解得多了一点。有一类View叫做AdapterView,比如ListView就是AdapterView的一种。这类的
View有一个setAdapter(Adapter adapter)方法,通过这个方法,来update View。 
Java代码  
ListView listView = (ListView)findViewById(R.layout.list_view);  
listView.setAdapter(new MyAdapter(this));  

上面的listView在初始化的时候是没有数据的,也就是没有ListItem,通过setAdapter()方法,才填充了数据。 

Adapter类的作用,就是根据数据源,来返回View,每个View最终会成为AdapterView的其中一个Child View。如同其名字一样,
Adapter是一个数据到视图的适配器。可以将其理解为是一个容器的处理器,它将容器内的每一项,映射成一个View并返回。
数据源可以是资源文件指定的数据集合,也可以来自SQLite,也可以是内存中的自定义对象……总之是一种数据对象的集合。而将
要返回的View,是由Adapter中的getView()方法创建并返回的,可以来自layout文件的设置,也可以是自定义的View对象。 

搞清楚原理,就明白AdapterView和Adapter是怎样协同工作,生成页面的了。Adapter根据数据源,循环调用getView()方
法,生成View对象的集合。然后AdapterView.setAdapter()方法,用前面生成的View对象集合,来给视图生成数据项。
ListView只是AdapterView的一个子类,还有很多其他的AdapterView,但是原理都是相同的。有空的话可以看看
ListAdapter类的getView()方法的源码,应该会对这个过程理解得更清晰一些。 

4. 搞清楚了上一点,再来看ListActivity,其实ListActivity只是一个普通的Activity,只是它内置了一个ListView,
以及提供了getListView()方法来获取内置的ListView;还提供了setListAdapter()方法,来给这个内置的ListView设置
Adapter;以及诸如onListItemClick()等方法而已。在要使用ListActivity的地方,用普通的Activity也是完全可以的,
只是要多写一些方法而已。 

5. 在前面的博客里提到过,关于UI Thread有2个原则。1:不要阻塞UI Thread,2:不要在UI Thread之外操作视图组件。
今天补充一点,除了进一步封装的AsyncTask类之外,android主要是通过提供Handler类来支持这一点的。 
Java代码  
private final Handler handler = new Handler(){  
    public void handleMessage(Message msg){  
        doSomeThingWithView();// 在UI Thread中对视图组件做操作  
    }  
};  
  
public void someMethod(){  
    new Thread(){  
        public void run(){  
            someLongTimeJob();// 在worker Thread中做一些耗时操作,以免阻塞UI thread  
            handler.sendMessage();// 发送消息到UI Thread  
        }  
    }.start();  
}  

通过以上的代码结构,来遵循上述的2个原则。当然,用AsyncTask来处理,代码会更加简洁一些。不过看到比较多的源代码
,还是采用这种结构来做的。其他方式都不太好,或者有阻塞UI Thread,造成ANR的问题。或者是在worker Thread中操作
了View组件,可能隐藏了一些多线程情况下的BUG 

Handler就是android提供的,不同线程之间通信的一种方式。用无参数构造器的方式创建Handler实例,则将其绑定到当前
线程(UI Thread)的Looper中。Looper是和Handler紧密相关的一个类,开发者一般不会直接操作Looper类。这个Looper
内部维护一个Message Queue,会对每条Message进行处理。没有看过源码,不过我估计这个过程应该是异步的。 

6. 关于Layout布局,现在我还搞得不是太清楚,也是目前最薄弱的一块,只能先简单理解一下书上说的内容。 

首先每种ViewGroup都有一个内部类LayoutParams,这个LayoutParams有一个继承体系,比如ViewGroup.
LayoutParams->ViewGroup.MarginLayoutParams->LinearLayout.LayoutParams->TableLayout
.LayoutParams 

然后貌似每个组件的最终Layout是由其Parent决定的,而其自身的Layout又决定了它的Child的Layout布局,这是一个递
归的过程。 

A child View requests a size, and the parent makes a decision on how to position the child 
view on the screen. The child makes a request and the parent makes the decision. Child elements
 do keep track of what size they're initially asked to be, in case layout is recalculated when
 things are added or removed, but they can't force a particular size. Because of this, View
 elements have two sets of dimensions: the size and width they want to take up [getMeasuredWidth
()] and the actual size they end up after a parent's decision [getWidth()]. 

暂时就知道这么多,以后再补充一点。我个人感觉这是android中比较难的一部分,可能是以前我没做过swing的开发,所以不太
好类比。android布局和我熟悉的html+css布局差别还是挺大的 

7. View组件还有一个focus的概念。即虽然页面上有很多组件,但同时只能有一个组件获取到焦点,来响应用户输入。用户的操
作,可能导致焦点的改变。这点和html一样,很好理解。通常“下一焦点”是由android系统决定的,但是开发者也可以通过在XML
中设置nextFocusDown等属性,来改变默认行为。另外View也有requestFocus()方法,来请求获取焦点。类似jquery中的$
(element).focus() 

8. android中的事件响应机制类似设计模式中的“观察者模式”,一个事件分2个阶段:the component raising the event
 and the component(or components) that respond to the event. 前者类似Button.setOnClickListener
(OnClickListener listener),后者类似listener.onClick(View v)。这里要再次重
申一下,由于View接口是单线程的,所以无论何时,要记得只能在UI Thread中调用View上的方法(包括更新View) 

9. 程序中用到的资源,统一放在res文件夹下,res文件夹下的子目录是有命名规范的,乱命名就认不出来(也就是,无法编译,
然后通过R.java来引用)。比如res/drawable下放图片,res/layout下放布局XML,res/values下放strings、colors
、styles、arrays等,具体的可以看google的API文档。然后这些资源文件会由aapt工具编译成binary,之后程序中就可以通过
R Class来获取这些编译后的资源 

10. Resources类是所有资源的统一入口,提供了如getStringArray(int),openRawResource(int),getXml(int)等
方法
Android垃圾回收实质内容解析
Android手机操作系统中的代码编写方式对于有基础的编程人员来说是比较容易的。因为它是基于Linux平台的操作系统。我们在这里为大家介绍的是Android垃圾回收这一机制,以加深大家对这一系统的了解。

  个人觉得sp和wp实际上就是Android 为其c++实现的自动垃圾 回收机制 ,具体到内部实现,sp和wp实际上只是一个实现垃圾回收功能的接口而已,比如说对*,->的重载,是为了其看起来跟真正的指针一样,而真正实现垃圾回收的是refbase这个基类。这部分代码的目录在:/frameworks/base/include/utils/RefBase.h

  首先所有的类都会虚继承refbase类,因为它实现了达到Android垃圾回收所需要的所有function,因此实际上所有的对象声明出来以后都具备了自动释放自己的能力,也就是说实际上智能指针就是我们的对象本身,它会维持一个对本身强引用和弱引用的计数,一旦强引用计数为0它就会释放掉自己。

  首先我们看sp,sp实际上不是smart pointer的缩写,而是strong pointer,它实际上内部就包含了一个指向对象的指针而已。我们可以简单看看sp的一个构造函数:

  1.template< typename T>

  2.sp< T>::sp(T* other)

  3.: m_ptr(other)

  4.{

  5.if (other) other->incStrong(this);

  6.}

  比如说我们声明一个对象:

  1.sp< CameraHardwareInterface> hardware(new CameraHal());

  实际上sp指针对本身没有进行什么操作,就是一个指针的赋值,包含了一个指向对象的指针,但是对象会对对象本身增加一个强引用计数,这个 incStrong的实现就在refbase类里面。新new出来一个CameraHal对象,将它的值给 sp< CameraHardwareInterface>的时候,它的强引用计数就会从0变为1。因此每次将对象赋值给一个sp指针的时候,对象的强引用计数都会加1,下面我们再看看sp的析构函数:

  1.template< typename T>

  2.sp< T>::~sp()

  3.{

  4.if (m_ptr) m_ptr->decStrong(this);

  5.}

  实际上每次delete一个sp对象的时候,sp指针指向的对象的强引用计数就会减一,当对象的强引用技术 为0的时候这个对象就会被自动释放掉。

  我们再看wp,wp就是weak pointer的缩写,弱引用指针的原理 ,就是为了应用Android垃圾回收来减少对那些胖子对象对内存的占用,我们首先来看wp的一个构造函数:

  1.wp< T>::wp(T* other)

  2.: m_ptr(other)

  3.{

  4.if (other) m_refs = other->createWeak(this);

  5.}

  它和sp一样实际上也就是仅仅对指针进行了赋值而已,对象本身会增加一个对自身的弱引用计数,同时wp还包含一个m_ref指针,这个指针主要是用来将wp升级为sp时候使用的:

  1.template< typename T>

  2.sp< T> wp< T>::promote() const

  3.{

  4.return sp< T>(m_ptr, m_refs);

  5.}

  6.template< typename T>

  7.sp< T>::sp(T* p, weakref_type* refs)

  8.: m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)

  9.{

  10.}

  实际上我们对wp指针唯一能做的就是将wp指针升级为一个sp指针,然后判断是否升级成功,如果成功说明对象依旧存在,如果失败说明对象已经被释放掉了。wp指针我现在看到的是在单例中使用很多,确保mhardware对象只有一个,比如:

  1.wp< CameraHardwareInterface> CameraHardwareStub::singleton;

  2.sp< CameraHardwareInterface> CameraHal::createInstance()

  3.{

  4.LOG_FUNCTION_NAME

  5.if (singleton != 0) {

  6.sp< CameraHardwareInterface> hardware = singleton.promote();

  7.if (hardware != 0) {

  8.return hardware;

  9.}

  10.}

  11.sp< CameraHardwareInterface> hardware(new CameraHal()); //强引用加1

  12.singleton = hardware;//弱引用加1

  13.return hardware;//赋值构造函数,强引用加1

  14.}

  15.//hardware被删除,强引用减1
Android的智能指针
在Android的源代码中,经常会看到形如:sp<xxx>、wp<xxx>这样的类型定义,这其实是Android中的智能 指针。智能指针是C++中的一个概念,通过基于引用计数的方法,解决对象的自动释放的问题。在C++编程中,有两个很让人头痛的问题:一是忘记释放动态申 请的对象从而造成内存泄露;二是对象在一个地方释放后,又在别的地方被使用,从而引起内存访问错误。程序员往往需要花费很大精力进行精心设计,以避免这些 问题的出现。在使用智能指针后,动态申请的内存将会被自动释放(有点类似Java的垃圾回收),不需要再使用delete来释放对象,也不需要考虑一个对 象是否已经在其它地方被释放了,从而使程序编写工作减轻不少,而程序的稳定性大大提高。

  Android的智能指针相关的源代码在下面两个文件中:

  frameworks\base\include\utils\RefBase.h

  frameworks\base\libs\utils\RefBase.cpp

  涉及的类以及类之间的关系如下图所示:



  Android中定义了两种智能指针类型,一种是强指针sp(strong pointer),一种是弱指针(weak pointer)。其实称为强引用和弱引用更合适一些。强指针与一般意义的智能指针概念相同,通过引用计数来记录有多少使用者在使用一个对象,如果所有使 用者都放弃了对该对象的引用,则该对象将被自动销毁。

  弱指针也指向一个对象,但是弱指针仅仅记录该对象的地址,不能通过弱指针来访问该对象,也就是说不能通过弱智真来调用对象的成员函数或访问对象的成员变 量。要想访问弱指针所指向的对象,需首先将弱指针升级为强指针(通过wp类所提供的promote()方法)。弱指针所指向的对象是有可能在其它地方被销 毁的,如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。

  是不是很神奇?弱指针是怎么做到这一点的呢?其实说穿了一点也不复杂,原因就在于每一个可以被智能指针引用的对象都同时被附加了另外一个 weakref_impl类型的对象,这个对象中负责记录对象的强指针引用计数和弱指针引用计数。这个对象是智能指针的实现内部使用的,智能指针的使用者 看不到这个对象。弱指针操作的就是这个对象,只有当强引用计数和弱引用计数都为0时,这个对象才会被销毁。

  说了这么多原理,下面该看看到底智能指针该怎么使用了。假设现在有一个类MyClass,如果要使用智能指针来引用这个类的对象,那么这个类需满足下列两个前提条件:

  (1) 这个类是基类RefBase的子类或间接子类;

  (2) 这个类必须定义虚构造函数,即它的构造函数需要这样定义:

  virtual ~MyClass();

  满足了上述条件的类就可以定义智能指针了,定义方法和普通指针类似。比如普通指针是这样定义:

  MyClass* p_obj;

  智能指针是这样定义:

  sp<MyClass> p_obj;

  注意不要定义成 sp<MyClass>* p_obj。初学者容易犯这种错误,这样实际上相当于定义了一个指针的指针。尽管在语法上没有问题,但是最好永远不要使用这样的定义。

  定义了一个智能指针的变量,就可以象普通指针那样使用它,包括赋值、访问对象成员、作为函数的返回值、作为函数的参数等。比如:

  p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass>

  sp<MyClass> p_obj2 = p_obj;

  p_obj->func();

  p_obj = create_obj();

  some_func(p_obj);

  注意不要试图delete一个智能指针,即 delete p_obj。不要担心对象的销毁问题,智能指针的最大作用就是自动销毁不再使用的对象。不需要再使用一个对象后,直接将指针赋值为NULL即可:

  p_obj = NULL;

  上面说的都是强指针,弱指针的定义方法和强指针类似,但是不能通过弱指针来访问对象的成员。下面是弱指针的示例:

  wp<MyClass> wp_obj = new MyClass();

  p_obj = wp_obj.promote(); // 升级为强指针。不过这里要用.而不是->,真是有负其指针之名啊

  wp_obj = NULL;

  智能指针用起来是很方便,在一般情况下最好使用智能指针来代替普通指针。但是需要知道一个智能指针其实是一个对象,而不是一个真正的指针,因此其运行效率是远远比不上普通指针的。所以在对运行效率敏感的地方,最好还是不要使用智能指针为好。
Global site tag (gtag.js) - Google Analytics