Category Porady i solucje

My (very short) experience with startMonitoringForRegion method

Since iOS 4.0 there is cool method in CoreLocation framework:

- (void)startMonitoringForRegion:(CLRegion *)region desiredAccuracy:(CLLocationAccuracy)accuracy

that can send events (even while application is in background) when phone is entering or leaving specified area.

But there are two “small” disadvantages:

1) Works only on iphone 4 and newer
2) It’s based on cell position (not GPS!) so accuracy is about houndreds of meters

Could be so beautiful.. :)

SSL connection lag on Android

While I was developing a Homer application for Android, I met frustrating problem with delays on ssl connection. Homer uses ssl for every http request, so I decided use HttpsURLConnection class for this job. Everything was fine until you run Homer in LAN without access to internet. Than, unexpectedly, every request started taking about 5 to 10 seconds. If you connect the internet, everything was fine back.

I resigned from HttpsUrlConnection and made directly communication using sockets. However, it didn’t solve problem – every call to SSLSocket.getSession() took the same 5 to 10 seconds as before.

Finaly, I found solution. There is a bug in Android prior to 4.0 version, which makes reverse DNS lookup for every https request. So, when you have no access to dns servers, it takes long time. To fix problem you can use code from:

http://code.google.com/p/android/issues/detail?id=13117#c14

Voila!

Galaxy Tab 10.1 and DatagramSocket problem

While working with sockets on Android you may face the problem with InvalidArgumentException thrown when you try to create a new instance of DatagramSocket:

InetSocketAddress bindInetAddr = new InetSocketAddress(bindAddr, bindPort);
DatagramSocket ssdpUniSock = new DatagramSocket(bindInetAddr);

The second line causes InvalidArgumentException error. Fortunately, the solution exists and is quite simple. Instead of creating an instance of DatagramSocket, you can obtain in from DatagramChannel and then bind an address:

DatagramChannel channel = DatagramChannel.open();
DatagramSocket ssdpUniSock = channel.socket();
InetSocketAddress bindInetAddr = new InetSocketAddress(bindAddr, bindPort);
ssdpUniSock.bind(bindInetAddr);

Voila, done!

Androida zabawy z wibratorem

Zagadka. Jak w naszym programie dostać się do sterowania wibratorem? Krótkie poszukiwania i znajdujemy klasę android.os.Vibrator, ale nie ma ona publicznego konstruktora, a więc utworzenie obiektu w ten sposób:

Vibrator v = new Vibrator();

się nie powiedzie. Aby otrzymać obiekt Vibrator, potrzebujemy użyć metody getSystemService, którą znajdziemy w klasie android.content.Context.

Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);

Ale to nie wszystko, jeżeli nie chcemy otrzymać wyjątku na klatę, powinniśmy w pliku AndroidManifest.xml zaznaczyć, że aplikacja będzie używała wibratora. W tym celu do tego pliku dopisujemy:

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

Dla jasności cały przykładowy plik AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="kubik.game.antris"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-permission android:name="android.permission.VIBRATE"/>
 
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
        <activity android:name=".Antris"
                  android:label="@string/app_name"
				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
				  >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
<uses-sdk android:minSdkVersion="7"></uses-sdk>
</manifest>

Zrobione. Teraz już możemy wibrować:

Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(500); //wibruj pół sekundy

Debugowanie na emulatorze…

..to nie to, co wilki lubią najbardziej. Przesiadłem się na prawdziwie sprzętowe rozwiązanie i komfort tworzenia aplikacji wzrósł wielokrotnie. Wybrałem Samsunga i5700 – niedrogi telefon, a przy tym z Androidem 2.1 oraz oferujący bezproblemową współpracę z eclipse. Jak ta współpraca wygląda w praktyce? Bajecznie – wybiera się w Eclipse opcję Run, a pisany program w kilka sekund instaluje się i uruchamia na komórce. Genialne :)

No, teraz emulator służyć będzie tylko do testowania programów na innych konfiguracjach.

Cykliczne wywoływanie metody w klasie

Ponieważ piszę dynamiczną grę, więc nie obejdzie się bez cyklicznego wywoływania jakiejś metody, która będzie chociażby przesuwała klocki na ekranie, wyświetlała animacje, odgrywała dźwięki czy robiła inne fascynujące rzeczy. Jak na razie odkryłem dwa sposoby na cykliczne uruchamianie wskazanej metody.

Pierwszy z nich to stworzenie własnego handlera do komunikatów:

class RefreshHandler extends android.os.Handler {
 
       @Override
       public void handleMessage(Message msg) {
           NaszaKlasa.this.update();
       }
 
       public void sleep(long delayMillis) {
           this.removeMessages(0);
           sendMessageDelayed(obtainMessage(0), delayMillis);
       }
};

oraz w NaszaKlasa (nie mylić z nk.pl) odpowiedniej metody, którą handler będzie wywoływał:

public class NaszaKlasa extend View {
    private RefreshHandler mRedrawHandler = new RefreshHandler();
 
    public void update() {
       mRedrawHandler.sleep(100);
    }
}

Aby zainicjować cykliczne wykonywanie się metody update() należy ją pierwszy raz wywołać “ręcznie”; następnie będzie ona wywoływana co zadany interwał czasu (w przykładzie co 100ms) z poziomu klasy RefreshHandler.

Drugi, to użycie klasy java.util.Timer – ten sposób wydaje się początkowy bardziej oczywisty, jednak jest o tyle niewygodny, że wymaga rozszerzenia naszej klasy o TimerTask. Do mojej aplikacji wybrałem więc pierwsze rozwiązanie, jednak wcale nie będę zaskoczony, gdy okaże się, że istnieje inne, lepsze rozwiązanie. Z jednej strony dokumentacja androida mówi, że jednym z głównych celów, do których są używane handlery, jest “wykonywanie pewnych zdarzeń w przyszłości”, a ponieważ jest to klasa z API androida, więc powinna być odpowiednio zoptymalizowana do zadań, które wykonuje. Z drugiej strony, do takich celów zwykle używało się właśnie klas podobnych do klasy Timer, ale może pora na zmianę przyzwyczajeń. :)

O handlerach poczytacie więcej w dokumentacji: http://developer.android.com/reference/android/os/Handler.html

Co to jest za obiekt R i dlaczego R cannot be resolve

Gdy utworzymy w eclipse nowy projekt na androida, w głównej klasie zostanie wygenerowana metoda onCreate, która będzie wyglądała podobnie do tego co poniżej:

public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
}

Niepokoić może odwołanie do obiektu R i wyświetlany na starcie błąd R cannot be resolve. Kilka minut zajęło mi rozwiązanie tej zagadki, a więc teraz w kilka minut spróbuję napisać jej wyjaśnienie :)

Otóż, podczas budowania projektu, automatycznie w folderze gen/ tworzona jest klasa R, zawierające statyczne definicje wszystkich obiektów, które umieścimy w folderze res/. Ponieważ dopiero co utworzyliśmy nowy projekt, więc klasa R nie została jeszcze wygenerowana i stąd na ekranie widzimy błąd. Wystarczy jednak zbudować projekt i po problemie.

Do czego służy ta klasa? Otóż dzięki niej, możemy odwoływać się do obiektów umieszczonych w folderze res/. Dajmy na to, że do folderu res/drawable/ mamy wrzucony obrazek ball1.png – wtedy w klasie R zostanie utworzony odpowiedni wpis:

public final class R {
    public static final class drawable {
           public static final int ball1=0x7f020000;
    }
}

Teraz, aby użyć gdzieś w kodzie nasz obrazek, wystarczy odwołać się do obiektu R.drawable.ball1, np. w taki sposób:

ImageView i = new ImageView(this);
i.setImageResource(R.drawable.ball1);

No, to było takie wytłumaczenie totalnego laika dla laików :) Więcej o zasobach można poczytać pod adresem http://developer.android.com/guide/topics/resources/index.html oraz w dokumentacji http://developer.android.com/reference/android/content/res/package-summary.html.

java.lang.OutOfMemoryError: PermGen space

Kolejny problem z jakim przyszło mi się zmierzyć już na starcie zabawy z androidem, był kolejny błąd, jakim raczył mnie Eclipse. W losowych momentach wyskakiwał komunikat java.lang.OutOfMemoryError: PermGen space i całe środowisko albo się wieszało, albo eclipse się zamykał.

Jak na razie pomogło dopisania do eclipse.ini (znajdujący się w głównym katalogu, w którym zainstalowano eclipse) ustawienia:

-XX:MaxPermSize=256m

tak wygląda obecnie u mnie cały plik eclipse.ini:

-startup
plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-product
org.eclipse.epp.package.jee.product
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
-vmargs
-Dosgi.requiredJavaVersion=1.5
-XX:MaxPermSize=256m
-Xms40m
-Xmx512m

Project is missing required source folder: ‘gen’

Jest to często spotykany w Eclipse problem, podczas próby zbudowania projektu na androida – pojawiający się komunikat “Project is missing required source folder: 'gen'” pomimo tego, że taki folder na dysku istnieje. Czasami na taki błąd pomaga rzeczywiste usunięcie tego folderu z dysku i wykonanie Project -> Clean.

Jednak skutecznym rozwiązaniem, które pozwoli zapomnieć o tym komunikacie jest ustawienie wersji kompilatora Javy dla projektu: Project -> Properties, wybieramy zakładkę Java Compiler i włączamy opcję Enable project specific settings oraz odpowiednio ustawiamy Compiler compliance level.

Konwersja zapisu wykładniczego do liczby całkowitej w PHP

W jaki najprostszy sposób skonwertować liczbę zapisaną w formacie wykładniczym (naukowym), np.:

3.625e+8

do postaci liczby całkowitej? Tu, podobnie jak w starym dobrym języku C, przychodzi nam na pomoc funkcja sscanf(), która parsuje parametr wejściowy zgodnie z podanym formatem. Rozwiązanie dla naszego przypadku jest:


$number = '3.625e+8';
$result = sscanf($number, '%e');
print $result[0]; //wyświetla 362500000

Copyright © Kubiczek devblog
I'm lovin' it ;)

Built on Notes Blog Core
Powered by WordPress