20189200餘超 2018-2019-2 移動平臺應用開發實踐第十一週做業

20189200餘超 2018-2019-2 移動平臺應用開發實踐第十一週做業

製做視頻

本節是Android多媒體基本API調用的一節,帶來的是MediaRecord的簡單使用, 用法很是簡單,咱們寫個例子來熟悉熟悉~
java

佈局代碼:activity_main.xml:android

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_control"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="開始錄音" />

</RelativeLayout>

MainActivity.java:程序員

public class MainActivity extends AppCompatActivity {

    private Button btn_control;
    private boolean isStart = false;
    private MediaRecorder mr = null;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_control = (Button) findViewById(R.id.btn_control);
        btn_control.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!isStart){
                    startRecord();
                    btn_control.setText("中止錄製");
                    isStart = true;
                }else{
                    stopRecord();
                    btn_control.setText("開始錄製");
                    isStart = false;
                }
            }
        });
    }


    //開始錄製
    private void startRecord(){
        if(mr == null){
            File dir = new File(Environment.getExternalStorageDirectory(),"sounds");
            if(!dir.exists()){
                dir.mkdirs();
            }
            File soundFile = new File(dir,System.currentTimeMillis()+".amr");
            if(!soundFile.exists()){
                try {
                    soundFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            mr = new MediaRecorder();
            mr.setAudioSource(MediaRecorder.AudioSource.MIC);  //音頻輸入源
            mr.setOutputFormat(MediaRecorder.OutputFormat.AMR_WB);   //設置輸出格式
            mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_WB);   //設置編碼格式
            mr.setOutputFile(soundFile.getAbsolutePath());
            try {
                mr.prepare();
                mr.start();  //開始錄製
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    //中止錄製,資源釋放
    private void stopRecord(){
        if(mr != null){
            mr.stop();
            mr.release();
            mr = null;
        }
    }
}

最後別忘了在AndroidManifest.xml中添加下述權限:數據庫

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

處理Handler

方法一:(java習慣,在android平臺開發時這樣是不行的,由於它違背了單線程模型)
剛剛開始接觸android線程編程的時候,習慣好像java同樣,試圖用下面的代碼解決問題編程

new Thread( new Runnable() {     
    public void run() {     
         myView.invalidate();    
     }            
}).start();

能夠實現功能,刷新UI界面。可是這樣是不行的,由於它違背了單線程模型:Android UI操做並非線程安全的而且這些操做必須在UI線程中執行。api

方法二:(Thread+Handler)安全

查閱了文檔和apidemo後,發覺經常使用的方法是利用Handler來實現UI線程的更新的。網絡

Handler來根據接收的消息,處理UI更新。Thread線程發出Handler消息,通知更新UI。異步

Handler myHandler = new Handler() {  
          public void handleMessage(Message msg) {   
               switch (msg.what) {   
                    case TestHandler.GUIUPDATEIDENTIFIER:   
                         myBounceView.invalidate();  
                         break;   
               }   
               super.handleMessage(msg);   
          }   
     };
class myThread implements Runnable {   
          public void run() {  
               while (!Thread.currentThread().isInterrupted()) {    
                       
                    Message message = new Message();   
                    message.what = TestHandler.GUIUPDATEIDENTIFIER;   
                      
                    TestHandler.this.myHandler.sendMessage(message);   
                    try {   
                         Thread.sleep(100);    
                    } catch (InterruptedException e) {   
                         Thread.currentThread().interrupt();   
                    }   
               }   
          }   
     }

方法三:(java習慣。Android平臺中,這樣作是不行的,這跟Android的線程安全有關)ide

在Android平臺中須要反覆按週期執行方法可使用Java上自帶的TimerTask類,TimerTask相對於Thread來講對於資源消耗的更低,除了使用Android自帶的AlarmManager使用Timer定時器是一種更好的解決方法。 咱們須要引入import java.util.Timer; 和 import java.util.TimerTask;

public class JavaTimer extends Activity {  
  
    Timer timer = new Timer();  
    TimerTask task = new TimerTask(){   
        public void run() {  
            setTitle("hear me?");  
        }            
    };  

    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
       
         timer.schedule(task, 10000);  

    }  
}

方法四:(TimerTask + Handler)

經過配合Handler來實現timer功能的!

public class TestTimer extends Activity {  
  
    Timer timer = new Timer();  
    Handler handler = new Handler(){   
        public void handleMessage(Message msg) {  
            switch (msg.what) {      
            case 1:      
                setTitle("hear me?");  
                break;      
            }      
            super.handleMessage(msg);  
        }  
          
    };  

    TimerTask task = new TimerTask(){    
        public void run() {  
            Message message = new Message();      
            message.what = 1;      
            handler.sendMessage(message);    
        }            
    };  

    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
      
        timer.schedule(task, 10000);  
    }  
}

方法五:( Runnable + Handler.postDelayed(runnable,time) )
在Android裏定時更新 UI,一般使用的是 java.util.Timer, java.util.TimerTask, android.os.Handler組合。實際上Handler 自身已經提供了定時的功能。

private Handler handler = new Handler();  
  
    private Runnable myRunnable= new Runnable() {    
        public void run() {  
             
            if (run) {  
                handler.postDelayed(this, 1000);  
                count++;  
            }  
            tvCounter.setText("Count: " + count);  

        }  
    };

不少初入Android或Java開發的新手對Thread、Looper、Handler和Message仍然比較迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask因爲目前市面上的書籍等資料都沒有談到這些問題,今天就這一問題作更系統性的總結。咱們建立的Service、Activity以及Broadcast均是一個主線程處理,這裏咱們能夠理解爲UI線程。可是在操做一些耗時操做時,好比I/O讀寫的大文件讀寫,數據庫操做以及網絡下載須要很長時間,爲了避免阻塞用戶界面,出現ANR的響應提示窗口,這個時候咱們能夠考慮使用Thread線程來解決。

對於從事過J2ME開發的程序員來講Thread比較簡單,直接匿名建立重寫run方法,調用start方法執行便可。或者從Runnable接口繼承,但對於Android平臺來講UI控件都沒有設計成爲線程安全類型,因此須要引入一些同步的機制來使其刷新,這點Google在設計Android時卻是參考了下Win32的消息處理機制。

  1. 對於線程中的刷新一個View爲基類的界面,可使用postInvalidate()方法在線程中來處理,其中還提供了一些重寫方法好比postInvalidate(int left,int top,int right,int bottom) 來刷新一個矩形區域,以及延時執行,好比postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一個參數爲毫秒

  2. 固然推薦的方法是經過一個Handler來處理這些,能夠在一個線程的run方法中調用handler對象的 postMessage或sendMessage方法來實現,Android程序內部維護着一個消息隊列,會輪訓處理這些,若是你是Win32程序員能夠很好理解這些消息處理,不過相對於Android來講沒有提供 PreTranslateMessage這些干涉內部的方法。

  3. Looper又是什麼呢? ,其實Android中每個Thread都跟着一個Looper,Looper能夠幫助Thread維護一個消息隊列,可是Looper和Handler沒有什麼關係,咱們從開源的代碼能夠看到Android還提供了一個Thread繼承類HanderThread能夠幫助咱們處理,在HandlerThread對象中能夠經過getLooper方法獲取一個Looper對象控制句柄,咱們能夠將其這個Looper對象映射到一個Handler中去來實現一個線程同步機制,Looper對象的執行須要初始化Looper.prepare方法就是昨天咱們看到的問題,同時推出時還要釋放資源,使用Looper.release方法。

4.Message 在Android是什麼呢? 對於Android中Handler能夠傳遞一些內容,經過Bundle對象能夠封裝String、Integer以及Blob二進制對象,咱們經過在線程中使用Handler對象的sendEmptyMessage或sendMessage方法來傳遞一個Bundle對象到Handler處理器。對於Handler類提供了重寫方法handleMessage(Message msg) 來判斷,經過msg.what來區分每條信息。將Bundle解包來實現Handler類更新UI線程中的內容實現控件的刷新操做。相關的Handler對象有關消息發送sendXXXX相關方法以下,同時還有postXXXX相關方法,這些和Win32中的道理基本一致,一個爲發送後直接返回,一個爲處理後才返回 .

  1. java.util.concurrent對象分析,對於過去從事Java開發的程序員不會對Concurrent對象感到陌生吧,他是JDK 1.5之後新增的重要特性做爲掌上設備,咱們不提倡使用該類,考慮到Android爲咱們已經設計好的Task機制,這裏不作過多的贅述,相關緣由參考下面的介紹:

  2. 在Android中還提供了一種有別於線程的處理方式,就是Task以及AsyncTask,從開源代碼中能夠看到是針對Concurrent的封裝,開發人員能夠方便的處理這些異步任務。

異步工具

用Java方法來實現異步
主要有兩種方法來實現異步,繼承Thread類和實現Runnable接口

一、繼承Thread類

public class MyThread extends Thread{
    private String str;
    public MyThread(String str){
        this.str= str;
    }
    @Override
    public void run(){
        System.out.println(str);
        runOnUIThread(new Runnable() {
            //此處更新UI
        });
    }
}
public class MainText{
    public static void main(String[] args){
        MyThread thread1= new MyThread("myThread1");
        MyThread thread2= new MyThread("myThread2");
        MyThread thread3= new MyThread("myThread3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

二、實現Runnable接口

public class MyThreadRunnable implements Runnable{
    private String str;
    public MyThreadRunnable(String str){
        this.str = str;
    }
    @Override
    public void run(){
        System.out.print(str);
        runOnUIThread(new Runnable(){
            //此處更新UI
        }
    }
}

public class MainTest{
    public static void main(String[] args){
        MyThreadRunnable thread1 = new MyThreadRunnable("runnable1");
        MyThreadRunnable thread2 = new MyThreadRunnable("runnable2");
        Thread t1 = new Thread(thread1);
        Thread t2 = new Thread(thread2);
        t1.start();
        t2.start();
    }
}

//或者直接  使用內部類:
new Thread(new Runnable(){
    @Override
    public void run(){
        System.out.print("執行耗時或者異步操做");
        runOnUIThread(new Runnable(){
            //此處更新UI
        });
    }
});

使用Android 特有的方法實現異步
一、AsyncTask
實現一個內部類並繼承AsyncTask類,並重寫方法

public class MainActivity extends Activity{
    private ProgressBar mProgressBar;
    private ImageView mImageView;
    @Override
    public void onCreate(................){
        MyAsyncTask myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute("www.baidu.com/xxx.jpg");
    }
}

class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>{
    @Override
    protected void onPreExecture(){
        //這裏是開始線程以前執行的,是在UI線程
        mProgressBar.setMax(100);
        super.onPreExecute();
    }
    @Override
    protected Bitmap doInBackground(String... params){
        //這是在後臺子線程中執行的
        Bitmap bitmap = null;
        try{
            URL url = new URL(params[0]);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            InputStream stream = connection.getInputStream();
            bitmap = BitmapFactory.decodeStream(inputStrem);
            publishProgress(70); //這裏是更新進度
            inputStream.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return bitmap;
    }
    @Override
    protected void onCancelled(){
        //當任務被取消時回調
        super.onCancelled();
    }
    @Override
    protected void onProgressUpdate(Integer... values){
        super.onP......(values);
        //更新進度
        mProgressBar.setProgress(values[0]);
    }
    @Override
    protected void onPostExecute(Bitmap bitmap){
        super............(bitmap);
        //當任務執行完成時調用,在UI線程
        mImageView.setImageBitmap(bitmap);
    }

相關文章
相關標籤/搜索