`

Best Practices for Background Jobs

 
阅读更多

Best Practices for Background Jobs

       本文这些课程将告诉你如何在后台运行任务以提高你的app的性能和最小化你的电池消耗。

 

      

Running in a Background Service

 如何将任务提交给在后台运行的Service来处理来提供UI性能和app的响应

  1. Creating a Background Service
  2. Sending Work Requests to the Background Service
  3. Reporting Work Status

 

Loading Data in the Background

 如和使用CursorLoader查询数据而不影响UI响应   

  1. Running a Query with a CursorLoader
  2. Handling the Results

  

Managing Device Awake State

 如何使用重复alarms和唤醒锁来运行后台任务

  1. Keeping the Device Awake
  2. Scheduling Repeating Alarms

 

      Running in a Background Service

        除非你专门指定,否则你在你的app里做的几乎所有操作都运行在前台,运行在一个专门的叫做UI线程的特定线程里。这可能引起问题,因为UI线程里耗时操作将中断用户界面的响应。这使得你的用户恼怒,甚至引起系统错误。为了避免这,Android框架提供了几个类帮组你减轻操作负担,这些类能让它们运行在各自的后台线程里。这些类里最有用的是IntentService

        本节描述如何实现IntentService,如何开启执行请求,以及返回执行结果给其他的组件。

       

        Creating a Background Service

        学习如何产生IntentService

       Sending Work Requests to the Background Service

        学习如何向IntentService发送执行请求

        Reporting Work Status

       学习如何使用Intent和LocalBroadcastManager来通信 来自于activity的IntentService的执行请求的状态

 

     Creating a Background Service

        IntentService类提供了运行单个后台线程操作的简单易懂的结构。这使得IntentService能处理耗时操作而不影响用户界面响应。还有,IntentService几乎不受用户界面生命周期事件的影响,因此,在某些情况下,AsyncTask将关闭和终止运行,而IntentService能继续运行。

        IntentService有如下几个限制:

  •  它不能和用户界面直接的交互。为了将操作结果更新到UI,你必须将结果发送给Activity
  •   执行请求按序运行。如果一个操作正在IntentService运行,这时你发送另一个执行请求,该请求将等待   直到第一个操作完成
  •    运行在IntentService里的操作不能被中断

        然而,大多数的情况下,对简单的后台操作来说,IntentService是一个更好的方式。

 

       本节讲述如何产生你自己的IntentService子类,也告诉你如何产生被要求实现的回调方法onHandleIntent(),最后,讲述如何在manifest文件里定义IntentService。              

      Create an IntentService

        为了产生IntentService组件,定义一个类继承IntentService,在该类里面重写onHandleIntent()回调方法,如下:       

public class RSSPullService extends IntentService {
    @Override
    protected void onHandleIntent(Intent workIntent) {
        // Gets data from the incoming Intent
        String dataString = workIntent.getDataString();
        ...
        // Do work here, based on the contents of dataString
        ...
    }
}

       注意,一个普通的Service的其他的回调方法例如onStartCommand()等自动地被IntentService调用,在IntentService里,你应该避免重写这些回调方法。

 

       IntentService也需要在你的app的manifest文件里声明入口。通过<service>元素声明,<service><application>的子元素。

        

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name">
        ...
        <!--
            Because android:exported is set to "false",
            the service is only available to this app.
        -->
        <service
            android:name=".RSSPullService"
            android:exported="false"/>
        ...
    <application/>

         属性android:name指定了IntentService的类名。

 

        注意<service>元素并没有包含一个意图过滤器。向该IntentService发送执行请求的activity使用显示意图。因此,不需要过滤器。这也意味着仅仅在同一个app或者有相同的用户ID的app里的组件能访问该Service。

       现在你已实现了基本的IntentService类,你能通过Intent对象向其发送执行请求。具体发送执行请求的过程下节将描述。

    

 

      Sending Work Requests to the Background Service

       上节课告诉你如何产生IntentService类。这节课告诉你如何通过发送Intent来触发IntentService来运行操作。Intent可能包含发送给IntentServive处理的数据。你也能在Activity或者Fragment里的任何地方发送Intent到IntentService。

 

      Create and Send a Work Request to an IntentService

       为了产生执行请求,并且发送该请求到IntentService,产生一个显示的Intent,添加执行请求数据到该Intent里,通过调用startService()方法来发送该执行请求到IntentService。

       下面的片段表明了这:

       1.为名为RSSPullService的IntentService产生一个新的、显示的意图       

/*
 * Creates a new Intent to start the RSSPullService
 * IntentService. Passes a URI in the
 * Intent's "data" field.
 */
mServiceIntent = new Intent(getActivity(), RSSPullService.class);
mServiceIntent.setData(Uri.parse(dataUrl));

       2.调用startService()     

// Starts the IntentService
getActivity().startService(mServiceIntent);

     注意,你能在Activity或者Fragment的任何地方发送执行请求。例如,如果你首先需要获取用户输入,你能在响应按钮点击或者相似的手势操作的回调处发送该请求。

 

       一旦你调用了startService(),IntentService执行onHandleIntent()方法里定义的工作,然后自我停止。

 

      下步是报告执行结果给先前的Activity或者Fragment。下节告诉你如何使用BroadcastReceiver来完成结果返回。

 

     Reporting Work Status

       本节讲述如何报告在后台servicve里运行的执行请求的状态给发送请求的组件。例如,这允许你返回执行的结果给Activity对象的UI。发送和接收状态的推荐方式是使用 LocalBroadcastManager,该类能保证你自己的app组件能接收到广播Intent对象,而其他app的组件将不会收到该Intent。

      Report Status From an IntentService

       为了发送在IntentService里执行的操作的结果给其他的组件,首先产生一个包含状态数据的Intent。可选地,你能添加action和数据URI到Intent。

       紧接着,通过调用LocalBroadcastManager.sendBroadcast()发送Intent。这将发送该Intent给你的app的任何注册接收该intent的组件。为了得到LocalBroadcastManager的实例,调用getInstance()。

      例如:

       

public final class Constants {
    ...
    // Defines a custom Intent action
    public static final String BROADCAST_ACTION =
        "com.example.android.threadsample.BROADCAST";
    ...
    // Defines the key for the status "extra" in an Intent
    public static final String EXTENDED_DATA_STATUS =
        "com.example.android.threadsample.STATUS";
    ...
}
public class RSSPullService extends IntentService {
...
    /*
     * Creates a new Intent containing a Uri object
     * BROADCAST_ACTION is a custom Intent action
     */
    Intent localIntent =
            new Intent(Constants.BROADCAST_ACTION)
            // Puts the status into the Intent
            .putExtra(Constants.EXTENDED_DATA_STATUS, status);
    // Broadcasts the Intent to receivers in this app.
    LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
...
}

         下一步是在发送执行请求的组件里处理接收到的广播Intent对象。        

      Receive Status Broadcasts from an IntentService

        为了接收广播Intent对象,使用BroadcastReceiver的子类。在该子类里,实现BroadcastReceiver. onReceive() 回调方法,LocalBroadcastManager在接收到广播Intent时将回调该方法。   LocalBroadcastManager将接收到的Intent传递给BroadcastReceiver.onReceive()

      例如:

       

// Broadcast receiver for receiving status updates from the IntentService
private class ResponseReceiver extends BroadcastReceiver
{
    // Prevents instantiation
    private DownloadStateReceiver() {
    }
    // Called when the BroadcastReceiver gets an Intent it's registered to receive
    @
    public void onReceive(Context context, Intent intent) {
...
        /*
         * Handle Intents here.
         */
...
    }
}

         一旦你已定义了 BroadcastReceiver,你能定义匹配特定action、categories和数据的过滤器,为了做这,产生一个IntentFilter。下面第一个片段表明了如何定义该过滤器:

        

// Class that displays photos
public class DisplayActivity extends FragmentActivity {
    ...
    public void onCreate(Bundle stateBundle) {
        ...
        super.onCreate(stateBundle);
        ...
        // The filter's action is BROADCAST_ACTION
        IntentFilter mStatusIntentFilter = new IntentFilter(
                Constants.BROADCAST_ACTION);
    
        // Adds a data filter for the HTTP scheme
        mStatusIntentFilter.addDataScheme("http");
        ...

        为了注册BroadcastReceiver 和IntentFilter 到系统,得到LocalBroadcastManager的实例,调用他的 registerReceiver()方法。下面的片段显示了如何注册BroadcastReceiver和他的过滤器:

        

        // Instantiates a new DownloadStateReceiver
        DownloadStateReceiver mDownloadStateReceiver =
                new DownloadStateReceiver();
        // Registers the DownloadStateReceiver and its intent filters
        LocalBroadcastManager.getInstance(this).registerReceiver(
                mDownloadStateReceiver,
                mStatusIntentFilter);
        ...

       一个 BroadcastReceiver能处理不止一种类型的广播Intent对象,每一个有它自己的action。这个特性允许你对于每个action运行不同的代码,不需要为每个action单独定义一个BroadcastReceiver。为了对于相同的 BroadcastReceiver定义另一个IntentFilter,重复调用 registerReceiver()方法,例如:

        

        /*
         * Instantiates a new action filter.
         * No data filter is needed.
         */
        statusIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE);
        ...
        // Registers the receiver with the new filter
        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
                mDownloadStateReceiver,
                mIntentFilter);

         发送一个广播Intent并不开启或者resume一个activity。一个Activity的BroadcastReceiver即使在你的app在后台的时候也会接收和处理Intent对象,但是并不会迫使你的app到前台。如果当你的app不可见时你想要通知用户一个发生在后台的事件时,使用Notification。对应于接收一个广播intent将从不start一个Activity。

 

 

 

Loading Data in the Background

    从ContentProvider里查询你想要展示的数据可能比较耗时。如果你直接在Activity里执行查询操作,可能让UI阻塞,而引起系统ANR。即使没有ANR,用户也可能明显的感到UI延迟。为了避免这些问题,你应该在单独的线程里初始化query,等待查询操作完成后在展示查询结果。

    为了实现这样的需求,你可以使用CursorLoader类。该类能在后台异步的执行查询操作,然后当查询完成后reconnects to 你的Activity。除了能执行后台查询操作外,CusorLoader能在该查询的数据发生改变时,能自动的再次运行查询操作。

    本课告诉你如何使用CursorLoader执行后台查询,本文的例子需要使用V4 Support Library,该包能向下兼容到Android 1.6。    

    Lessons

      Running a Query with a CursorLoader

          Learn how to run a query in the background, using a CursorLoader.

      Handling the Results

           Learn how to handle the Cursor returned from the query, and how to remove references to the currentCursor when the loader framework re-sets the CursorLoader.

   

    Running a Query with a CursorLoader

     CursorLoader能针对ContentProvider在后台执行异步查询,然后返回结果给调用它的Activity或者FragmentActivity。这允许Activity或者FragmentActivity继续和用户交互而查询仍在继续。    

    Define an Activity That Uses CursorLoader

     为了在Activity或者FragmentActivity里使用CursorLoader,使用LoaderCallbacks<Cursor>接口。CursorLoader回调该接口里定义的方法和Activity和FragmentActivity交互。本节和下章节将详细讲叙每个回调方法。

    例如,下面告诉你如何使用支持库的CursorLoader类定义一个FragmentActivity。通过继承FragmentActivity,你能像获取Fragment一样,来获取对CursorLoader的支持:

 

public class PhotoThumbnailFragment extends FragmentActivity implements
        LoaderManager.LoaderCallbacks<Cursor> {
...
}    

     Initialize the Query

      为了初始化查询任务,调用LoaderManager.initLoader().这初始化了后台框架。你能在用户键入了查询数据后做这,或者,在没有任何用户数据的情况下,你能在onCreate() 或者onCreateView()做初始化操作。例如:

     

    // Identifies a particular Loader being used in this component
    private static final int URL_LOADER = 0;
    ...
    /* When the system is ready for the Fragment to appear, this displays
     * the Fragment's View
     */
    public View onCreateView(
            LayoutInflater inflater,
            ViewGroup viewGroup,
            Bundle bundle) {
        ...
        /*
         * Initializes the CursorLoader. The URL_LOADER value is eventually passed
         * to onCreateLoader().
         */
        getLoaderManager().initLoader(URL_LOADER, null, this);
        ...
    }
      注:方法getLoaderManager()仅仅在Fragment类里有效,为了在FragmentActivity里获取到LoaderManager,调用getSupportLoaderManager()

 

     Start the Query

       只要后台框架已初始化,系统会调用你的onCreateLoader()实现。为了开始查询,从该方法返回一个CursorLoader对象。你能实例一个空的CursorLoader,然后使用它的方法定义你的查询,或者你能实例化该对象的同时定义查询任务。

      

/*
* Callback that's invoked when the system has initialized the Loader and
* is ready to start the query. This usually happens when initLoader() is
* called. The loaderID argument contains the ID value passed to the
* initLoader() call.
*/
@Override
public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle)
{
    /*
     * Takes action based on the ID of the Loader that's being created
     */
    switch (loaderID) {
        case URL_LOADER:
            // Returns a new CursorLoader
            return new CursorLoader(
                        getActivity(),   // Parent activity context
                        mDataUrl,        // Table to query
                        mProjection,     // Projection to return
                        null,            // No selection clause
                        null,            // No selection arguments
                        null             // Default sort order
        );
        default:
            // An invalid id was passed in
            return null;
    }
}
     

 

    Handling the Results

     一旦查询的后台框架有CursorLoader实例对象,它开始在后台查询。当查询完成,后台框架调用onLoadFinished(),这将在下节描述。

      上一章节已介绍了,你应该在CursorLoader类的onCreateLoader()实现方法里开始加载你的数据。然后CursorLoader在LoaderCallbacks.onLoadFinished()方法里提供查询结果给Activity或者FragmentActivity。  LoaderCallbacks.onLoadFinished()方法传入的参数之一是包含查询结果的Cursor对象。你能使用该对象更新你的数据展示或者进行进一步的操作。

     除了onCreateLoader()onLoadFinished()回调方法你需要实现外,你也必须实现onLoaderReset()方法。当CursorLoader检测到与该Cursor相关的数据已发生改变时该方法会被调用。当数据发生改变时,也将重新运行当前的查询。

    Handle Query Results

     为了展示CursorLoader返回的游标数据,使用一个实现了AdapterView的View类,给该类提供一个实现了CursorAdapter类的Adapter。然后系统自动的从Cursor获取数据给View。

    你能在没有任何数据时,就在View和Adapter之间设置联系。然后在onLoadFinished()方法里,你move一个Cursor进入到你的adapter。一旦你move Curosr到adapter,系统会自动的更新View。当你改变游标的内容时,也会自动更新View。

    例如:

    

public String[] mFromColumns = {
    DataProviderContract.IMAGE_PICTURENAME_COLUMN
};
public int[] mToFields = {
    R.id.PictureName
};
// Gets a handle to a List View
ListView mListView = (ListView) findViewById(R.id.dataList);
/*
 * Defines a SimpleCursorAdapter for the ListView
 *
 */
SimpleCursorAdapter mAdapter =
    new SimpleCursorAdapter(
            this,                // Current context
            R.layout.list_item,  // Layout for a single row
            null,                // No Cursor yet
            mFromColumns,        // Cursor columns to use
            mToFields,           // Layout fields to use
            0                    // No flags
    );
// Sets the adapter for the view
mListView.setAdapter(mAdapter);
...
/*
 * Defines the callback that CursorLoader calls
 * when it's finished its query
 */
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    ...
    /*
     * Moves the query results into the adapter, causing the
     * ListView fronting this adapter to re-display
     */
    mAdapter.changeCursor(cursor);
}
     无论在什么时候CursorLoader的cursor变得无效时,CursorLoader将被reset。这通常在与Cursor相关的data改变时发生。在再次执行查询之前,框架调用你实现的onLoaderReset()方法。为了避免内存泄漏,在该回调方法里,你应该删除所有的到当前Cursor的引用。一旦 onLoaderReset() 完成,CursorLoader会再次执行查询操作。例如:

 

    

/*
 * Invoked when the CursorLoader is being reset. For example, this is
 * called if the data in the provider changes and the Cursor becomes stale.
 */
@Override
public void onLoaderReset(Loader<Cursor> loader) {
    
    /*
     * Clears out the adapter's reference to the Cursor.
     * This prevents memory leaks.
     */
    mAdapter.changeCursor(null);
}
 

 

 

 

 

    Managing Device Awake State

      当一部Android设置被闲置时(is left idle),它首先将变暗,然后黑屏,最终关掉(turn of )CPU。这是为了保证设备的电量不被很快的耗尽。然而,有时,你的app可能需要不一样的行为:

  • Apps可能需要保持屏幕亮着,例如游戏和电影app。
  • 其他的一些可能不需要屏幕一直亮着,但需要CPU保持运行知道关键的操作执行完。

    本课告诉你如何在必须保持设备awake的情况下,同时又不耗电。   

    Lessons

     Keeping the Device Awake

           Learn how to keep the screen or CPU awake as needed,while minimizing the impact on battery life

    Scheduling Repeating Alarms

           Learn how to use repeating alarms to schedule operations that take place outside of the lifetime of the application,even if the application is not running and/or the device is asleep

   

     Keeping the Device Awake

       为了省点,空闲的Android设备很快的变得休眠。然而,有时app需要唤醒屏幕或者CPU来完成一些工作。

       你采取的实现方式依赖你的app的业务需要。然而,一般的规则是你应该尽量使用轻量级的实现方式,最小化你的app对系统资源的影响。下面的章节描述了如何处理这种情况。

     Keep the Screen On

       某些app需要保持屏幕亮着,例如游戏和视频app。实现该功能的最好的方式是在你的activity里(仅仅只在activity,绝不要在Servie或者其他的组件里)使用FLAG_KEEP_SCREEN_ON 。例如:

       

public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  }
         这种方式的优点是,不像wake locks(将被讨论在Keep the CPU On),它不需要特定的权限,平台能正确地管理用户在application之前的切换,同时你的app不需要担心未使用的资源不被释放。

 

        该方式的另一种实现是在你的application的xml布局文件里,使用 android:keepScreenOn定义:

       

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:keepScreenOn="true">
    ...
</RelativeLayout>
       使用android:keepScreenOn="true"等同于使用FLAG_KEEP_SCREEN_ON。你不管使用那种方式都行。在你的activity里编程地设置flag的优点是让你能有机会动态的clear该标志,因此当你想Screen关闭的时候能关闭掉。

 

       注:你不需要自己clear掉 FLAG_KEEP_SCREEN_ON 标志因为Window Manager会保证当app进入到后台或者切换前台时,系统和你的app不出错。除非在你的application正在运行时(例如,你想要屏幕在一定不活动的时间后黑屏)你不再想要屏幕亮着,类似这样特定的需求下,你才需要自己clear掉 FLAG_KEEP_SCREEN_ON 。如果你想要明确地clear该标志,然后允许屏幕关掉,使用clearFlags()

      getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON).

      Keep the CPU On

        如果你需要在设备进入休眠状态之前保持CPU一直处于运行状态而完成一些工作,你能使用 PowerManager系统服务调用wake locks。Wake locks允许你的app控制设备的power状态。

        产生和持有wake locks 对设备电量能有显著的影响。那么你应该在不得不需要时才使用wake locks,尽可能短时间的持有,尽可能早的释放。例如,你应该从不需要在一个activity里使用wake lock。如果你想要在activity里保持屏幕亮着,使用FLAG_KEEP_SCREEN_ON。

        一个使用wake lock的合理的情况可能是后台service,它需要持有wake lock保持CPU一直运行来做一些工作然而屏幕不关掉。还有,由于其对电量的影响,这种做法应尽量避免。

 

        一些使用wake locks的替代方案:

        为了使用wake lock,第一步是在你的manifest文件里添加WAKE_LOCK 权限:

   

 

<uses-permission android:name="android.permission.WAKE_LOCK" />
         如果你的app包含一个广播接收者,该广播接收者使用service做一些工作,你能通过WakefulBroadcastReceiver管理你的wake lock,像在Using a WakefulBroadcastReceiver里被描述的。这是一个更好的方式。如果你不使用该方式,下面显示了如何直接地设置一个wake lock:

 

     

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
Wakelock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
        "MyWakelockTag");
wakeLock.acquire();
         为了释放wake lock,调用wakelock.release().这释放了你对CPU的占有。为了避免耗电,你应该只要你的app已使用完了wake lock时就马上释放它。    

 

       Using WakefulBroadcastReceiver

        使用一个广播接收者,配合着servcie一起使用,能让你管理后台任务的生命周期。

        WakefulBroadcastReceiver是一个特定类型的广播接收者,它用来产生和管理你的app的PARTIAL_WAKE_LOCK 。一个WakefulBroadcastReceiver 传递work给一个Service(典型地,如一个IntentService),同时确保设备在过渡阶段里不变成休眠状态。当你传递一个work给Service执行时,如果你持有一个wake lock,这样设备可能在work完成前就变成休眠状态了。最终结果是在未来不确定的某一点app可能没有完成work而结束了,这当然不是你想要的。

       使用WakefulBroadcastReceiver的第一步是添加它到你的manifest文件里,如同其他的BroadcastReceiver声明的一样:

       

<receiver android:name=".MyWakefulReceiver"></receiver>
         下面的的代码使用startWakefulService()方法启动MyIntentService,相比于startService(),该方法的不同之处是当service开始时,WakefulBroadcastReceiver持有一个wake锁。使用方法传递给service的intent里含有一个标识该wake锁的extra数据:

 

        

public class MyWakefulReceiver extends WakefulBroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        // Start the service, keeping the device awake while the service is
        // launching. This is the Intent to deliver to the service.
        Intent service = new Intent(context, MyIntentService.class);
        startWakefulService(context, service);
    }
}
          当service被finished时,它调用MyWakefulReceiver.completeWakefulIntent() 释放wake lock。completeWakefulIntent()方法有一个intent参数对象,该参数就是刚才从WakefulBroadcastReceiver里开启service传入的intent对象。

 

         

public class MyIntentService extends IntentService {
    public static final int NOTIFICATION_ID = 1;
    private NotificationManager mNotificationManager;
    NotificationCompat.Builder builder;
    public MyIntentService() {
        super("MyIntentService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        // Do the work that requires your app to keep the CPU running.
        // ...
        // Release the wake lock provided by the WakefulBroadcastReceiver.
        MyWakefulReceiver.completeWakefulIntent(intent);
    }
}
 

 

     

 

     Scheduling Repeating Alarms   

       

        Alarms(基于AlarmManager类)提供了一种在你的application生命周期之外执行定时操作的方式。例如,你能使用alarm初始化一个耗时操作,例如一天一次开启一个service去下载天气预报信息。

        Alarms有如下这些特点:

  • 能让你发起意图在设定的时间和(或)时间间隔
  • 你能和BroadcastReceiver结合使用,来开启某个service或者执行特定操作。   
  • 这些操作能在你的app之外执行。因此你能使用它们触发events或者action,即使当你的app不是正在运行,或者甚至你的设备是休眠状态。
  • 它们能帮你最小化你的app的资源要求。你不需要依靠定时器或者常驻后台运行的service来调度任务操作。

     注意:对于确保在application的生命周期内的定时操作,推荐考虑使用Handler,配合Timer和Thread的使用来实现。这种方式使得Android更好的控制系统资源。

 

     像上面描述的,对于调度规则的event或者数据查询,重复的alarms是一个好的选择。一个重复的alarm有如下几个方面和特点:

  • alarm类型。更对的讨论,请参见Choose an alarm type
  • 触发时间。如果你指定的触发时间是已经过去,alarm将立即触发。
  • alarm的间隔。例如,一天一次、每个小时、每5秒等等。
  • 当alarm触发时一个pendIntent被绑定和触发。当你使用一个second alarm时,使用相同的pendIntent,它取代之前的alarm。

      如何设计你的repeating alarm对你的app如何使用系统资源有影响,甚至可能导致你的app滥用系统资源。例如,甚至对alarm特别仔细的管理都可能对电量有一个主要的影响。当你设计你的app时,遵循如下的向导:

  • 使得你的alarm频率最小化
  • 不是不得已时不要wake up 设备(该行为取决于alarm类型,像Choose an alarm type里被描述的)。
  • 你的alarm的触发时间不要比下面描述的还要精确:

        如果可能,使用setInexactRepeating() 方法而不是setRepeating()。当你使用setInexactRepeating()时,Android同步多个不inexact重复的alarm,然后同时fire这些alarm。这减少了电量消耗。

       如果你的alarm是基于一个间隔(例如,你的alarm一小时一次)而不是一个确切的触发时间(例如,alarm在7 a.m,然后每20分钟一次),使用ELAPSED_REALTIME类型的alarm。

 

        使用repeating alarm首先考虑的是使用什么类型的alarm。

         对应alarm,有两种时钟类型:"elapsed real time" and "real time clock" (RTC)。Elapsed real time使用“系统启动时间”作为参考,real time clock使用UTC(wall time)时间。这意味Elapsed real time适合设置基于时间流逝(例如,每30一次的alarm)因为它不受本地时间和时区的影响。real time clock可能更适合依赖于当前时区(locale)的alarm。

       这两个类型都有一个“wakeup”版本,那意思是说如果screen off时wake up设备的CPU。这确保了alarm在设定的时间被fire。如果你的app有时间依赖性这是有用的——例如,如果你有一个有限的Window来执行特别的操作。如果你不使用wakeup版本类型的alarm,那么所有repeating alarms将在你的设备再次awake时才会fire。

        如果你只是简单的需要一个特定间隔的alarm(例如,每半小时),使用真实的时间流逝类型(Elapsed real time type)。一般地,这是一个更好的选择。

        如果你需要一个每天特定时间的alarm,那么选择基于时钟的real time clock类型。注意,这种类型方式可能有一些缺点和陷阱(drawbacks)——app可能不能很好的转换到其他时区,如果用户改变了设备的时间设置,那将可能引起你的app不期望行为。

       如下列出了所以类型:

  • ELAPSED_REALTIME ——基于设备启动时间以来的时间量来fire pendIntent,但是不wake up设备。elapsed time包含了任何时间,即使设备是休眠状态的时间。
  • ELAPSED_REALTIME_WAKEUP ——wake up设备,从设备启动时间开始之后特定时间长度的时间后fire该pendIntent而触发响应的事件。
  • RTC ——在特定的时间fire PendIntent,而不wake up设备。
  • RTC_WAKEUP——在特定的时间Wake up设备来发起PendIntent。    

       ELAPSED_REALTIME_WAKEUP examples

        这儿是一些使用ELAPSED_REALTIME_WAKEUP的例子:

         30分钟后Wake up设备去发起alarm,然后每30分钟一次:

         

// Hopefully your alarm will have a lower frequency than this!
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
     

 

      Wake up the device to fire a one-time (non-repeating) alarm in one minute:

      

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() +
        60 * 1000, alarmIntent);

 

      RTC examples

      如下是一些使用RTC_WAKEUP的例子。

      在大约2:00p.唤醒设备来发起alarm,然后在每天相同的时间点一天一次:

      

// Set the alarm to start at approximately 2:00 p.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 14);

// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        AlarmManager.INTERVAL_DAY, alarmIntent);
 

 

      Wake up the device to fire the alarm at precisely 8:30 a.m., and every 20 minutes thereafter:

 

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);

// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        1000 * 60 * 20, alarmIntent);
  

 

     Decide how precise your alarm needs to be(决定你的alarm需要的精度)

      如上所述,选择alarm的类型常常是产生一个alarm的第一步。更进一步的区别是你需要你的alarm的精度。对于大多数的app, setInexactRepeating() 是正确的选择。当你使用该方法时,Android同步多个不精确的repeating alarm,然后同时fire这些alarm。这减少电量消耗。

       对于很少一部分app,可能对时间的要求比较严格——例如,alarm需要精准的在8:30发起,然后每隔一小时一次——使用setRepeating()。但是如果可能,你还是尽量不要使用exact alarm。

       使用setInexactRepeating(),你不能指定触发的特定的时间间隔。如果需要特定的时间间隔,你能使用setRepeating()。你必须使用时间间隔常量,例如INTERVAL_FIFTEEN_MINUTESINTERVAL_DAY等。查阅AlarmManager查看全部的常量列表。

       

    Cancel an Alarm

       取决于你的app,你可能需要cannel alarm。为了cannel一个alarm,调用Alarm Manager的cannel()方法,传递一个你不再想要fire的 PendingIntent。例如:

        

// If the alarm has been set, cancel it.
if (alarmMgr!= null) {
    alarmMgr.cancel(alarmIntent);
} 

 

    Start an Alarm When the Device Boots

      默认地,当设备关机时所有的alarm被取消。为了避免这发生,你能设计当设备重启时你的app自动地重启repeating alarm。这确保AlarmMananger能继续未完成的alarm任务而不需要用户手动重启alarm。

      步骤如下:

     1.在你的app的manifest文件里声明RECEIVE_BOOT_COMPLETED权限。这将能使你的app收到ACTION_BOOT_COMPLETED 广播 ,该广播将在系统完成booting后发送(这仅仅在app已启动一次时起作用):

       

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

 

       2.实现一个BroadcastReceiver 接收广播:

     

public class SampleBootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            // Set the alarm here.
        }
    }
}
      3.Add the receiver to your app's manifest file with an intent filter that filters on the  ACTION_BOOT_COMPLETED action:

 

         

<receiver android:name=".SampleBootReceiver"
        android:enabled="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"></action>
    </intent-filter>
</receiver>
       注意:在manifest文件里,boot receiver被设置为android:enabled="false"。这意味着只有app明晰地enable该receiver时,该receiver才会被调用。这阻止了boot receiver在不必要时被调起。你能如下enable一个receiver(例如,如果用户设置一个alarm):
      
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();

pm.setComponentEnabledSetting(receiver,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP);
        一旦你如此enable一个receiver,它将一直是enable状态,即使用户重启设备。换句话说,程序里编程方式enable了该receiver意味着重写了manifest里的设置,即使设备重启。该receiver将保存enable直到app disable它。如下所示,你能disable一个receiver(例如,如果用户cannel一个alarm):
     
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();

pm.setComponentEnabledSetting(receiver,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP);
 
 
分享到:
评论

相关推荐

    Troubleshooting.Ubuntu.Server.1785284142

    Discover tips and best practices to be followed for minimum maintenance of Ubuntu Server 3 Who This Book Is For This book is for a vast audience of Linux system administrators who primarily work on ...

    Python GUI Programming Cookbook Second Edition 2nd

    His previous jobs include working as a senior test automation engineer and designer for InfoGenesis (now Agilysys), QAD, InTouch Health, and FLIR Systems. Table of Contents Creating the GUI Form and...

    BC490 ABAP Performance Tuning.pdf

    理解并应用Best Practices,例如减少选择屏幕的输入字段,避免使用SELECT *,以及优化内部表的操作,都能显著提升代码效率。 其次,数据库优化是另一个核心环节。ABAP程序与数据库交互频繁,因此优化SQL语句至关...

    运动控制领域8轴插补运动控制源码:双DMA实现高频率脉冲输出与加减速控制

    内容概要:本文详细介绍了8轴插补运动控制系统的实现,重点探讨了双DMA技术的应用,实现了高频率脉冲输出(最高可达500kHz)。文中首先解释了双DMA的工作原理及其相对于传统脉冲输出方式的优势,即减少CPU负载并提高数据传输速率。接着阐述了8轴插补算法的设计思想,包括基于时间分割的方法来确定各轴在特定时间段内的脉冲数。此外,还讨论了加减速控制策略,尤其是S型加减速算法的应用,以确保运动的平顺性。最后,文章展示了具体的代码实现细节,涵盖DMA配置、插补算法、加减速控制等方面。 适合人群:从事运动控制系统开发的技术人员,尤其是对嵌入式系统有一定了解的研发人员。 使用场景及目标:适用于需要高精度、高频脉冲输出的工业应用场景,如工业机器人、3D打印、激光切割等。目标是帮助开发者理解和掌握8轴插补运动控制的关键技术和实现方法,从而应用于实际项目中。 其他说明:文中提供的代码示例主要基于STM32系列单片机,但相关概念和技术可以迁移至其他平台。同时,强调了硬件细节处理的重要性,如RC滤波电路的应用,以应对实际工程中的常见问题。

    2303040222橡胶232熊文栋(苯乙烯悬浮聚合)副本.pdf

    2303040222橡胶232熊文栋(苯乙烯悬浮聚合)副本.pdf

    音乐喷泉控制系统设计:基于Altium Designer的原理图与代码实现

    内容概要:本文详细介绍了音乐喷泉的设计与制作过程,涵盖了从原理图绘制到具体代码实现的各个方面。首先介绍了Altium Designer这款强大的电子设计软件,接着展示了如何利用现有文件进行设计,包括水泵控制、灯光效果和音乐解析三大核心模块的具体实现方法。文中提供了多个代码片段,如单片机控制喷头升降、PWM调速控制水泵以及灯光效果同步音乐节奏等。同时,强调了在实际制作过程中需要注意的问题,如焊接温度、布线规划、元件选择等。此外,还分享了一些实用技巧和经验教训,帮助读者更好地理解和应用相关知识。 适合人群:对电子设计感兴趣的爱好者、初学者以及有一定基础的电子工程师。 使用场景及目标:适用于希望深入了解音乐喷泉工作原理和技术实现的人群,目标是掌握如何使用Altium Designer完成音乐喷泉的电路设计,并能够编写相应的控制代码。 其他说明:文章不仅提供了详细的理论讲解,还包括了许多实战经验和技巧,有助于读者在实践中少走弯路。

    汽车主动悬架系统参数仿真与控制算法解析

    内容概要:本文详细介绍了汽车主动悬架系统的工作原理及其参数仿真的方法。首先解释了主动悬架的基本概念,即它可以根据车辆行驶状态和路面情况进行实时调整,提高行车安全性和舒适度。接着展示了如何利用简化的单自由度模型进行参数设置并进行仿真,具体涉及到了动力学方程、状态空间模型以及PID控制器的设计。此外还提到了更高级别的LQR控制器的应用,并强调了实际应用中需要注意的问题,如执行器响应延迟、物理限制等。文中通过实例演示了被动悬架与主动悬架在面对相同路面输入时的不同表现,突出了主动控制系统的优势。同时,针对传感器噪声处理、卡尔曼滤波器的使用、PWM信号生成等方面进行了深入探讨,揭示了主动悬架背后的复杂技术和工程挑战。 适用人群:对汽车工程特别是悬架系统感兴趣的研究人员和技术爱好者。 使用场景及目标:帮助读者理解主动悬架的工作机制,掌握基本的建模和仿真技能,为进一步开展相关领域的研究提供理论支持和技术指导。 其他说明:文中不仅提供了详细的数学推导和代码片段,还分享了许多实践经验,使读者能够全面地了解主动悬架系统的各个方面。

    Operating System 实验五 进程管理编程实验

    (3)请修改代码,解决临界区问题。解决后,无论如何运行,counter值均输出0

    少儿编程scratch项目源代码文件案例素材-Mc v2.zip

    少儿编程scratch项目源代码文件案例素材-Mc v2.zip

    车辆动力学联合仿真:基于Carsim和Simulink的十四自由度模型验证与优化

    内容概要:本文详细介绍了将Carsim与Simulink联合用于十四自由度车辆动力学模型的构建与验证过程。文中首先概述了整车架构的模块化分解方法,接着深入探讨了各个子系统的具体实现细节,如转向系统、轮胎模型、悬架子系统以及PI驾驶员控制器的设计与调优。针对联合仿真过程中遇到的关键问题,如采样率同步、参数调优、模型验证等进行了详细的讨论,并提供了具体的解决方案和技术技巧。通过对多种典型工况(如阶跃转向、正弦油门、双移线等)的仿真测试,验证了所建立模型的有效性和准确性。 适合人群:从事车辆动力学研究、汽车仿真领域的工程师和技术人员,尤其是那些希望深入了解Carsim与Simulink联合仿真的从业者。 使用场景及目标:适用于需要进行复杂车辆动力学仿真和模型验证的研究机构或企业。主要目标是提高仿真精度,缩短开发周期,确保模型能够准确反映实际车辆行为。此外,还可以作为教学材料帮助学生掌握先进的车辆建模技术和仿真工具。 其他说明:文中不仅分享了大量的实战经验和技巧,还附带了完整的源代码和详细的调试记录,对于想要深入理解和应用这一技术的人来说非常有价值。

    基于雨流计数法的源-荷-储双层协同优化配置及其Python实现

    内容概要:本文探讨了基于雨流计数法的源-荷-储双层协同优化配置,旨在提高能源系统的效率和经济性。文中介绍了双层优化架构,即外层优化储能系统的功率和容量,内层优化储能系统的充放电曲线并评估其寿命。通过Python代码示例展示了具体的实现过程,包括外层和内层优化的具体步骤以及雨流计数法的应用。此外,文章还讨论了常见的调试问题及解决方案,强调了内外层变量之间的相互影响。 适合人群:从事能源系统优化的研究人员和技术人员,尤其是对储能系统优化感兴趣的读者。 使用场景及目标:适用于需要进行源-荷-储系统优化的实际工程项目,如光伏电站、风力发电站等。目标是通过合理的储能配置,延长储能系统的使用寿命,降低成本,提高经济效益。 其他说明:文章提供了详细的代码示例和理论解释,帮助读者更好地理解和应用这一优化方法。同时提醒读者,在实际应用中需要注意数据的准确性以及参数的选择。

    维宏数控雕刻机 维宏3D卡驱动 Ncstudio V5.449

    很多盗版PCI卡都在用的雕刻机控制程序

    基于Matlab的三机并联风光储混合系统仿真及关键技术解析

    内容概要:本文详细介绍了三机并联的风光储混合系统在Matlab中的仿真方法及其关键技术。首先,针对光伏阵列模型,讨论了其核心二极管方程以及MPPT(最大功率点跟踪)算法的应用,强调了环境参数对输出特性的影响。接着,探讨了永磁同步风机的矢量控制,尤其是转速追踪和MPPT控制策略。对于混合储能系统,则深入讲解了超级电容和蓄电池的充放电策略,以及它们之间的协调机制。此外,还涉及了PQ控制的具体实现,包括双闭环结构的设计和锁相环的优化。最后,提供了仿真过程中常见的问题及解决方案,如求解器选择、参数敏感性和系统稳定性等。 适合人群:从事电力电子、新能源系统设计与仿真的工程师和技术人员,以及相关专业的研究生。 使用场景及目标:适用于希望深入了解风光储混合系统工作原理的研究人员,旨在帮助他们掌握Matlab仿真技巧,提高系统设计和优化的能力。 其他说明:文中不仅提供了详细的理论推导和代码示例,还分享了许多实践经验,有助于读者更好地理解和应用所学知识。

    基于NGSIM数据的Wiedemann99跟驰模型Matlab实现及其IPSO算法优化

    内容概要:本文详细介绍了基于NGSIM数据对Wiedemann99跟驰模型进行参数标定的过程。作者使用Matlab编写代码,实现了数据读取与预处理、Wiedemann99模型定义、拟合优度函数(RMSPE)计算以及改进粒子群算法(IPSO)。通过这些步骤,成功地对标定了Wiedemann99模型的关键参数,并对其进行了性能评估。文中不仅展示了具体的代码实现细节,还探讨了参数选择、算法改进等方面的经验教训。 适合人群:从事交通工程、智能交通系统研究的专业人士,尤其是那些对车辆跟驰行为建模感兴趣的科研工作者和技术开发者。 使用场景及目标:适用于需要精确模拟车辆跟驰行为的研究项目,如交通流量仿真、自动驾驶测试等。目标是提高模型的准确性和可靠性,以便更好地理解和预测真实的道路交通状况。 其他说明:文章提供了详细的代码片段和理论背景介绍,有助于读者深入理解整个标定流程。同时,作者分享了一些实用的小技巧,如参数敏感度分析、适应度函数设计等,对于相关领域的研究人员具有较高的参考价值。

    大模型落地路线图研究报告(2024年)

    内容概要:本文为中国信息通信研究院发布的《2024年大模型落地路线图研究报告》,旨在梳理大模型应用落地的共性需求和关键要素,为大模型赋能各行业提供参考。报告重点介绍了大模型应用落地的四个重要阶段——现状诊断、能力建设、应用部署、运营管理,归纳了八个关键步骤,包括能力分析、需求挖掘、方案设计、研发测试、应用开发、效能评估、运维监测和运营管理。报告详细分析了大模型在基础设施、数据资源、算法模型、应用服务、安全可信五个层面应重点关注的发展要素和亟待解决的问题。此外,报告还探讨了大模型在金融、工业、教育、医疗、政务等行业的具体应用场景及其带来的降本增效、提质增效等优势。最后,报告展望了大模型的发展趋势,强调了架构优化、行业数字化转型和可信发展的必要性。 适合人群:具备一定技术背景,特别是从事人工智能、大数据、云计算等领域工作的研发人员、管理人员和技术决策者。 使用场景及目标:①帮助企业和机构评估自身大模型应用的基础条件,明确业务转型需求;②指导大模型建设方案的设计和实施,确保技术选型的科学性和合理性;③提供应用部署和效能评估的具体方法,确保大模型在实际应用中的稳定性和高效性;④建立健全大模型的运营管理体系,保障业务的高效稳定开展。 其他说明:报告强调了大模型在推动各行业数字化转型中的重要作用,提出了未来大模型发展的重点方向,如架构优化、技术应用和可信发展。报告还呼吁社会各界共同关注大模型的安全可信问题,确保其与人类价值观的对齐,推动大模型的健康发展。

    少儿编程scratch项目源代码文件案例素材-Scratch泡泡龙.zip

    少儿编程scratch项目源代码文件案例素材-Scratch泡泡龙.zip

    软考初级程序员09-18年真题及答案解析

    软考初级程序员是中国计算机技术与软件专业技术资格(水平)考试中的一个重要级别,主要面向打算进入IT行业的初学者或初级程序员。这个级别的考试旨在测试考生的基础编程能力、计算机基础知识以及解决问题的能力。历年真题是备考的重要参考资料,可以帮助考生了解考试的题型、难度以及考点。 在"软考初级程序员09-18年真题及答案解析"的压缩包中,包含了从2009年至2018年上半年的所有程序员考试真题。这些真题涵盖了多个方面,包括但不限于: 1. **基础编程语言**:如C语言、Java、Python等,主要考察基本语法、数据类型、控制结构、函数使用等方面。 2. **数据结构与算法**:如数组、链表、栈、队列、树、图等,以及排序算法(冒泡、选择、插入、快速、归并等)和查找算法(线性查找、二分查找等)。 3. **计算机系统知识**:包括计算机组成原理、操作系统、网络基础知识,例如CPU结构、内存管理、进程与线程、网络协议等。 4. **数据库基础**:SQL语言的基本操作,如增删改查、子查询、联接操作、索引等。 5. **软件工程与项目管理**:软件生命周期、需求分析、设计原则、测试方法、版本控制等。 6. **法律法规与职业道德**:涉及知识产权、合同法、信息安全与隐私保护等。 每份真题后的答案解析部分,是对题目答案的详细解释,通常包括解题思路、关键步骤以及知识点的扩展。通过阅读解析,考生不仅能知道自己答案的正确与否,还能深入理解相关知识点,提高自己的分析和解决问题的能力。 在准备软考初级程序员考试时,考生应充分利用这些真题资源,进行模拟练习,掌握各类题目的解答技巧。同时,考生还需要广泛阅读教材,补充相关知识,提高对理论的理解。此外,多做编程实践,提高实际编程能力,也是非常重要的。 总结来说,这个压缩包是备考软考初级程序员的宝贵资料,它能帮助考生熟悉考试形式,了解重

    基于FPGA和W5500的TCP网络通信:Zynq扩展口开发测试平台(使用Vivado 2019.2纯Verilog实现)

    内容概要:本文详细介绍了如何在Zynq扩展口上使用FPGA和W5500实现稳定的TCP网络通信。作者通过一系列实验和技术手段,解决了多个实际问题,最终实现了零丢包的数据回环处理。主要内容包括:硬件搭建(SPI接口配置)、数据回环处理(双时钟域流水线)、压力测试(信号抓波形和防抖处理)、多路复用扩展以及上位机测试脚本的编写。文中提供了大量Verilog代码片段,展示了具体实现细节。 适合人群:具备一定FPGA开发经验的工程师,尤其是对TCP/IP协议栈感兴趣的嵌入式系统开发者。 使用场景及目标:适用于需要高性能、低延迟网络通信的应用场景,如工业控制系统、实时数据采集等。目标是帮助读者掌握在FPGA上实现高效TCP通信的方法和技术。 其他说明:文章不仅提供了详细的代码实现,还分享了许多实践经验,如SPI时钟优化、CS信号防抖、FIFO深度选择等。此外,作者还讨论了未来可能的改进方向,如UDP组播和QoS优先级控制。

    基于Matlab/Simulink的UKF/EKF实现路面附着系数估计

    内容概要:本文探讨了在汽车动力学研究和自动驾驶领域中,使用无迹扩展卡尔曼滤波(UKF/EKF)在Matlab/Simulink环境中对路面附着系数进行估计的方法。文中介绍了选择Matlab/Simulink的原因及其强大功能,详细解析了7自由度整车模型的构建,以及UKF和EKF的具体实现方式。UKF通过非线性处理和sigma点传播概率分布,适用于复杂工况;EKF则通过线性化处理,更适合计算资源有限的场景。两者在不同路面条件下表现出各自的优劣,如UKF在突变路面下表现更好,而EKF在不变路面上效率更高。此外,还讨论了调参技巧、工程实现细节及实际测试结果。 适用人群:从事汽车动力学研究、自动驾驶技术研发的专业人士,尤其是对非线性滤波算法感兴趣的研究人员和技术开发者。 使用场景及目标:①用于车辆稳定性控制系统中,提高行驶安全性;②优化滤波算法性能,平衡精度与实时性;③为复杂工况下的路面附着系数估计提供解决方案。 其他说明:文章不仅提供了理论分析,还包括大量代码示例和实践经验分享,有助于读者深入理解和实际应用。

    基于三菱PLC与触摸屏的定长送料系统:点动、相对定位与绝对定位的实现

    内容概要:本文详细介绍了如何使用三菱PLC(以FX3U为例)和显控触摸屏实现定长送料系统的三种核心功能:点动、相对定位和绝对定位。文章从硬件连接开始,逐步讲解了每种功能的具体实现方法,包括梯形图编程、参数设置以及触摸屏交互设计。特别强调了伺服和步进电机的应用,并提供了调试技巧和注意事项,确保系统稳定可靠。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要掌握PLC编程和伺服/步进电机控制的人群。 使用场景及目标:适用于各类需要精确控制物料输送的生产设备,如包装机、裁切设备等。目标是帮助工程师快速搭建稳定的定长送料系统,提高生产效率和产品质量。 其他说明:文中还分享了一些实战经验,如软限位设置、急停回路设计、电子齿轮比计算等,有助于解决实际应用中的常见问题。

Global site tag (gtag.js) - Google Analytics